1

I have a working executable here and I'm trying to build a DLL from the same code, but I'm having some strange errors and managed to pinpoint the culprits after a lot of trial an error, I decided to ask for help because I'm really stuck now. I'm linking both SDL2 and Cimgui (Dear Imgui C Wrapper) statically in my code, the executable works perfectly fine:

#include "imgui/imgui.h"
#include "cimgui.h"
#pragma comment(lib, "cimgui.lib")

#include "SDL.h"
#undef main
#pragma comment(lib, "SDL2-staticd.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "imm32.lib")
#pragma comment(lib, "version.lib")

void main()
{
    SDL_Init(0);
    igCreateContext(0);
    printf("OK");
}

So now I switch the project to DLL (I disabled that /IMPLIB option to make sure the only difference in the link command is the /DLL and the extension). When I try to build I get errors regarding cimgui.lib like:

cimgui.lib(imgui_draw.obj) : error LNK2001: unresolved external symbol memcmp
cimgui.lib(imgui_widgets.obj) : error LNK2001: unresolved external symbol memcpy
cimgui.lib(imgui.obj) : error LNK2001: unresolved external symbol memset

Apparently the Visual C runtime isn't being linked anymore, I don't really understand why, I could just add it immediately but let's step back and remove Cimgui from the code:

#include "SDL.h"
#undef main
#pragma comment(lib, "SDL2-staticd.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "imm32.lib")
#pragma comment(lib, "version.lib")

extern "C" __declspec(dllexport) void main()
{
    SDL_Init(0);
}

I get 13 errors similar to this:

MSVCRTD.lib(_init_.obj) : error LNK2019: unresolved external symbol _CrtDbgReport referenced in function _CRT_RTC_INIT

So I manually include the libs that contain the missing symbols: ucrtd.lib, vcruntimed.lib, and then the DLL builds fine.

Now I try to add Cimgui again, but it fails to build with errors like these:

MSVCRTD.lib(utility.obj) : error LNK2019: unresolved external symbol __vcrt_initialize referenced in function __scrt_initialize_crt
MSVCRTD.lib(utility.obj) : error LNK2019: unresolved external symbol __acrt_initialize referenced in function __scrt_initialize_crt

the __vcrt definitions are in libvcruntimed.lib, but the __acrt definitions are in libucrtd.lib, which conflicts with ucrtd.lib, and even if I remove ucrtd.lib altogether and try to link just libucrtd.lib, I get errors like:

ucrtd.lib(ucrtbased.dll) : error LNK2005: malloc already defined in libucrtd.lib(malloc.obj)

Which I don't know how to interpret because I'm not linking ucrtd.lib.

I don't really understand why building a DLL isn't just like building an Executable especially because the EXE binary is very similar to the DLL and I could even use it to interop as a DLL but that doesn't feel right. I'm sure there's a way to build this and I just don't know how.

For completeness (and just in case), here are the linker commands for the EXE and DLL:

/OUT:"C:\Projects\BuildTest\x64\Debug\Build.dll" /MANIFEST /NXCOMPAT /PDB:"C:\Projects\BuildTest\x64\Debug\Build.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /DLL /MACHINE:X64 /INCREMENTAL /PGD:"C:\Projects\BuildTest\x64\Debug\Build.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Debug\Build.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /LIBPATH:"C:\Projects\cimgui_build\Debug" /LIBPATH:"C:\Projects\SDL2-2.0.8\build\Debug" /TLBID:1 
/OUT:"C:\Projects\BuildTest\x64\Debug\Build.exe" /MANIFEST /NXCOMPAT /PDB:"C:\Projects\BuildTest\x64\Debug\Build.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X64 /INCREMENTAL /PGD:"C:\Projects\BuildTest\x64\Debug\Build.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Debug\Build.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /LIBPATH:"C:\Projects\cimgui_build\Debug" /LIBPATH:"C:\Projects\SDL2-2.0.8\build\Debug" /TLBID:1 

EDIT:

I made a "Minimal, Complete and Verifiable Example" (Self-contained - dependencies included, relative paths, VS2017 project, should compile directly provided you have Windows SDK) and published online here:

https://bitbucket.org/AlanGameDev/buildmvce_so/downloads/

(you can download and extract or clone if you prefer)

Alan
  • 33
  • 5
  • What is your `/ENTRY` setting? Did you rebuild or at least clean then build after modifying your project? Actually, this kind of problem requires an [MCVE] and for that you need to post your makefile or project file. – jwdonahue Oct 07 '18 at 03:12
  • Possible duplicate of [What is an undefined reference/unresolved external symbol error and how do I fix it?](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Ken White Oct 07 '18 at 03:37
  • @jwdonahue I made a MCVE and published online here: https://bitbucket.org/AlanGameDev/buildmvce_so/downloads/ (you can either download or clone directly if you prefer) – Alan Oct 07 '18 at 04:14
  • @KenWhite I'm afraid this isn't a duplicate of that since it's probably related to conflicting MSVCRT dependencies I guess... I don't know exactly what's the problem, but it's not simply missing symbols, as I said I know where are the symbols but can't link the files, and also I would like to know why building an EXE is so different than a DLL. – Alan Oct 07 '18 at 04:21
  • @jwdonahue I forgot to say that yes I always rebuild the whole shebang :) – Alan Oct 07 '18 at 04:22
  • @KenWhite that was just an intermediary step to explain what I've tried, as I said the definitions for the LNK2019 errors are in the libvcruntimed.lib and libucrtd.lib libraries, so that's not what's stopping me – Alan Oct 07 '18 at 04:26
  • `#pragma comment(lib, ...` is a bad habit IMHO. It won't work with other tool chains. Better to specify libraries to link in your build system. – Jesper Juhl Oct 07 '18 at 10:49
  • @JesperJuhl yes, I'm just using that because the repro is for MSVC and that way it's much easier imho to add/remove libs, but thanks for the heads up ;) – Alan Oct 07 '18 at 13:28

1 Answers1

1

If you set the linker flag /VERBOSE to both exe/dll builds and diff the results, you'd get a clue as to what's going on.

In the exe build you'd see

1>      Found mainCRTStartup
1>        Loaded MSVCRTD.lib(exe_main.obj)
...

1>      Found __xi_a
1>        Referenced in MSVCRTD.lib(exe_main.obj)
1>        Loaded MSVCRTD.lib(initializers.obj)
1>Processed /DEFAULTLIB:kernel32.lib
1> Processed /DISALLOWLIB:msvcrt.lib
1> Processed /DISALLOWLIB:libcmt.lib
1> Processed /DISALLOWLIB:libcmtd.lib
1> Processed /DISALLOWLIB:vcruntime.lib
1>Processed /DEFAULTLIB:vcruntimed.lib
1> Processed /DISALLOWLIB:libvcruntime.lib
1> Processed /DISALLOWLIB:libvcruntimed.lib
1> Processed /DISALLOWLIB:ucrt.lib
1>Processed /DEFAULTLIB:ucrtd.lib
1> Processed /DISALLOWLIB:libucrt.lib
1> Processed /DISALLOWLIB:libucrtd.lib

none of which appear in the dll build. In the dll build you'd see only -

1>      Found _DllMainCRTStartup
1>        Loaded SDL2-staticd.lib(SDL.obj)

Which is very weird. It seems SDL2 includes its own implementation of the entry point _DllMainCRTStartup, and since the linker takes it from there and not the crt lib - it misses a whole lot of useful /DEFAULTLIB pragmas.

Googling around it seems the _dllMainCRTStartup is included within SDL to satisfy some Watcom needs, and it was already suspected to cause trouble in MSVC.

If you build SDL from sources you should probably just comment out the _dllMainCRTStartup implementation and (hopefully) build successfully.

Ofek Shilon
  • 14,734
  • 5
  • 67
  • 101
  • Thanks. It is indeed the implementations in SDL that were causing the problem. There's a CMake flag for that btw. I still don't quite understand why that only happens when you build the DLL though. Do DLLs link all dependencies statically or something like that? – Alan Oct 16 '18 at 13:27
  • The default entry point *for an dll* is _dllMainCRTStartup, so it is required only for dll linkage. In regular builds the linker finds this entry point somewhere in MSVCRTD.lib (probably in dll_main.obj or similar), and i'm guessing this obj uses symbols that require initializers.obj. initializers.obj includes a bunch of linker directives like '/DEFAULTLIB:vcruntimed.lib' (probably as pragmas in the source), which cause the rest of the link to succeed. When you link agains SDL this entire train is taken off track - this is simply an SDL bug when building a dll on MSVC. – Ofek Shilon Oct 16 '18 at 15:32
  • BTW kudos for the small repro. It would have been impossible to investigate otherwise – Ofek Shilon Oct 16 '18 at 21:03
  • Thank you for your valuable help @OfekShilon. It's really appreciated. – Alan Oct 17 '18 at 01:37