0

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.

AbeMonk
  • 110
  • 7
  • If you pass the Visual C++ linker only one of the two import libraries, does it produce "unresolved external" errors or does it think all 4 functions are provided by the one library? – Ben Voigt Jul 24 '23 at 18:09
  • Your DLL creation command is also missing a % placeholder (it has test1.dll hardcoded instead) – Ben Voigt Jul 24 '23 at 18:10
  • @BenVoigt You mean the MinGW one? The percent placeholder is just to minimize the amount of code I need to show in the question. I never actually use the percent placeholder in the build command. I'll rewrite that part as it may have been a bit unclear. I see what you mean though. – AbeMonk Jul 24 '23 at 19:32
  • @BenVoigt It works fine if I pass it just one, because then the functions from the other library that cause the problem aren't there in the first place. – AbeMonk Jul 24 '23 at 19:35
  • I'm thinking it may have to do with the import libraries being in some way incorrect? The DLLs both seem to work if I use only one of the two import libraries. – AbeMonk Jul 24 '23 at 19:36
  • @BenVoigt In case I misinterpreted what you meant, the program does give "unresolved external" errors if I remove one import library *and* still leave all 4 function declarations in the program. – AbeMonk Jul 24 '23 at 19:38
  • What's weird is that `140021268` and `1400C66C8` are the same for both DLL import tables. The loader should be able to resolve the conflict in preferred addresses, but it might be worth trying to set different preferred base address in each DLL. I'm also wondering if you should be setting a flag like `-fPIC` to guarantee the DLLs contain relocatable code. – Ben Voigt Jul 24 '23 at 19:56
  • I constructed the import library using Microsoft's tools instead of MinGW's, and that seemed to work. Somehow it took me a while to discover that was possible. – AbeMonk Jul 24 '23 at 20:37

1 Answers1

0

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.

AbeMonk
  • 110
  • 7