MSVC - 可执行文件构建正确但 DLL 不正确,缺少符号和冲突的依赖关系 - SSCCE/Repro

MSVC - Executable builds correctly but DLL doesn't, missing symbols and conflicting dependencies - SSCCE/Repro

我这里有一个工作的可执行文件,我正在尝试从相同的代码构建一个 DLL,但是我遇到了一些奇怪的错误,并且在多次尝试错误后设法查明了罪魁祸首,我决定寻求帮助,因为我现在真的被困住了。我在我的代码中静态地 linking SDL2 和 Cimgui(亲爱的 Imgui C 包装器),可执行文件工作得很好:

#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");
}

所以现在我将项目切换到 DLL(我禁用了 /IMPLIB 选项以确保唯一的区别 link 命令是 /DLL 和扩展名)。当我尝试构建时,我收到有关 cimgui.lib 的错误,例如:

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

显然不再 link 编辑 Visual C 运行时,我真的不明白为什么,我可以立即添加它,但让我们退后一步,从代码中删除 Cimgui:

#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);
}

我收到 13 个与此类似的错误:

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

所以我手动包含了包含缺失符号的库:ucrtd.libvcruntimed.lib,然后 DLL 构建正常。

现在我再次尝试添加 Cimgui,但构建失败并出现如下错误:

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

__vcrt定义在libvcruntimed.lib中,但是__acrt定义在libucrtd.lib中,这与ucrtd.lib冲突,即使我删除ucrtd.lib 并尝试 link 只是 libucrtd.lib,我得到如下错误:

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

我不知道如何解释,因为我不是 linking ucrtd.lib

我真的不明白为什么构建 DLL 不像构​​建可执行文件,尤其是因为 EXE 二进制文件与 DLL 非常相似,我什至可以将它作为 DLL 进行互操作,但事实并非如此感觉不错。我确定有一种方法可以构建它,但我只是不知道如何构建。

为了完整性(以防万一),这里是 EXE 和 DLL 的 linker 命令:

/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 

编辑:

我做了一个"Minimal, Complete and Verifiable Example"(自包含-包括依赖项,相对路径,VS2017项目,如果你有Windows SDK应该直接编译)并在线发布在这里:

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

(您可以根据需要下载并提取或克隆)

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.