Search code examples
cvisual-studiovisual-c++dllmingw

Visual C++ program imports functions from the wrong MinGW-created DLLs


I have a Visual C++ project compiled using Visual Studio. This program imports 2 DLLs that I created using the MinGW x64 cross-compiler on Ubuntu. For this question, I have created 2 contrived DLLs containing bogus functions (these contrived DLLs still cause the same issue). We'll call these test1.dll and test2.dll.

test1.c:

__declspec(dllexport) int test_func_add(int a, int b) { return a + b; }
__declspec(dllexport) unsigned long test_func_ptr(void* ptr_to_anything) { return (unsigned long)ptr_to_anything; }

test2.c:

__declspec(dllexport) int test_func_mult(int a, int b) { return a * b; }
__declspec(dllexport) double test_func_div(double dividend, double divisor) { return dividend / divisor; }

Both of these C files are compiled using the exact same MinGW command:

x86_64-w64-mingw32-gcc test1.c -o test1.dll -shared -fvisibility=hidden -Wl,--out-implib,test1.lib
x86_64-w64-mingw32-gcc test2.c -o test2.dll -shared -fvisibility=hidden -Wl,--out-implib,test2.lib

Using dumpbin /exports on the resulting test1.dll and test2.dll yields the following, respectively:

 ordinal hint RVA      name

  // Look at the "Edit" section at the bottom of the question
  1    0 000013D0 test_func_add
  2    1 000013E4 test_func_ptr

 ordinal hint RVA      name

  1    0 000013E3 test_func_div
  2    1 000013D0 test_func_mult

And on the resulting import libraries (test1.lib and test2.lib, respectively):

 ordinal    name

  // Look at the "Edit" section at the bottom of the question
  test_func_ptr
  test_func_add

 ordinal    name

  test_func_mult
  test_func_div

It seems like everything is fine?

However, when I link these DLLs into my Visual C++ application via their import libraries and run dumpbin /imports on the resulting executable, I get this:

test1.dll
         140021268 Import Address Table
         1400C66C8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                       1 test_func_div
                       1 test_func_add
                       2 test_func_ptr
                       2 test_func_mult

test2.dll
         140021268 Import Address Table
         1400C66C8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                       1 test_func_div
                       1 test_func_add
                       2 test_func_ptr
                       2 test_func_mult

Its trying to import the same functions from both DLLs. Obviously, it then looks in one DLL for a function, doesn't find it, and throws an entry point could not be found error even though that function does exist - just in the other DLL.

Lastly, here's how I declared these external functions in the Visual C++ project:

extern "C" __declspec(dllimport) int test_func_add(int a, int b);
extern "C" __declspec(dllimport) unsigned long test_func_ptr(void* ptr_to_anything);
extern "C" __declspec(dllimport) int test_func_mult(int a, int b);
extern "C" __declspec(dllimport) double test_func_div(double dividend, double divisor);

Why could this be occuring? Are MinGW and MSVC projects incompatible? Is my process wrong? Any help would be much appreciated.

Edit: Looking back over the outputs of dumpbin /exports, I noticed that the orderings of the functions are reversed on the import libraries when compared to their corresponding DLLs. Could this be part of the issue? I compared this to the orderings of opencv_world460.dll and opencv_world460.lib (my program uses OpenCV) and the orderings of the functions were the same in the import library and DLL.


By the way, I've already looked at this post. The OP had downloaded a different version import library than DLL, which doesn't answer my question as I created both the import library and DLL.


Solution

  • It turns out the problem did indeed have to do with the MinGW-generated import libraries. I solved the issue by using Microsoft's lib tool to generate the import libraries instead. This requires telling the MinGW linker to output a .def file, which can be done using the following command:

    x86_64-w64-mingw32-gcc test1.c -o test1.dll -shared -fvisibility=hidden -Wl,--output-def,test1.def
    x86_64-w64-mingw32-gcc test2.c -o test2.dll -shared -fvisibility=hidden -Wl,--output-def,test2.def
    

    Then, in a Visual Studio terminal (after copying the .dll and .def files to Windows):

    lib /DEF:test1.def /OUT:test1.lib /MACHINE:x64
    lib /DEF:test2.def /OUT:test2.lib /MACHINE:x64
    

    These commands will generate import libraries that actually work with the MSVC environment. To be clear, the dynamic libraries generated by MinGW were fine. It was only the import libraries that I needed to generate with official Microsoft tools.