我在 MSVC2017 中有以下场景:
- 带有函数
bool foo()
的静态库
- 一个链接到上面静态库的动态链接库
- 使用显式运行时链接加载动态链接库并通过
GetProcAddress
调用foo()
的应用程序>
在静态库中,foo()
定义如下:
extern "C" __declspec(dllexport) bool foo()
{
return true;
}
现在,因为 foo()
没有被动态链接库使用,所以当应用程序使用 GetProcAddress
时,它的符号不会被导出,因此无法找到。
我试过:
#pragma comment(linker, "/include:foo")
和:
#pragma comment(linker, "/export:foo")
如果我将定义移动到动态链接库(不是可行的解决方案),我可以使用 Dependency Walker 看到导出的 foo()
但是当我保留使用上述链接器开关在静态库中定义。我认为这是因为该符号仍未使用,因此无论如何仍未导出?
我想要一个适用于 Windows 上的 MSVC 和 Linux 上的 Clang 的解决方案。谢谢!
最佳答案
你做错了什么(或者至少不像你在问题中描述的那样)。当然,您在答案中发布的内容也有效,但这只是一种解决方法,因为“常规”方式应该有效。
这是一个小例子。
lib00.cpp:
extern "C" __declspec(dllexport) bool foo()
{
return true;
}
dll00.cpp:
extern "C" __declspec(dllexport) bool bar()
{
return false;
}
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056330888]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2017\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul [prompt]> [prompt]> dir /b dll00.cpp lib00.cpp [prompt]> [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib00.obj lib00.cpp lib00.cpp [prompt]> [prompt]> lib /nologo /out:lib00.lib lib00.obj [prompt]> [prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp dll00.cpp [prompt]> [prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.lib Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> dir /b dll00.cpp dll00.dll dll00.exp dll00.lib dll00.obj lib00.cpp lib00.lib lib00.obj [prompt]> [prompt]> dumpbin /nologo /exports dll00.dll Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001000 bar Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text [prompt]> [prompt]> :: ----- Re-link dll, instructing it to include foo ----- [prompt]> [prompt]> link /nologo /dll /include:foo /out:dll00.dll dll00.obj lib00.lib Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00.dll Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 00001010 foo Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text
注意事项:
如前所述,我使用了命令行,但 VStudio IDE
调用了相同的命令(更多参数)添加 /include:foo(第 2nd Link 命令)导出 foo 以及(如在下一个 DumpBin 输出中所见):
指定此选项等同于添加
#pragma comment(linker, "/include:foo")
(在 dll.cpp/em> - 或任何直接传递给链接器的文件)/export:foo 不是必需的,因为函数已经由 __declspec(dllexport)
导出
我没有走到最后(应用程序),因为 foo 出现在 DumpBin 输出中就足够了(它也可以从 中看到依赖行者)
更新#0
毕竟你可能没有做错事。但请记住,它不可扩展(如果您有数百个此类符号)。看着[MS.Learn]: Overview of LIB , 它在导出内容方面提供与 Link 相同的选项。但他们似乎被忽略了。
当构建一个 .lib 时,也许有人想在链接时指定要包含的所有符号(通过选项或通过 #pragma comment),当构建 .lib,而不是在链接时构建。显然,它们被忽略了(我已经测试过了),除非在直接传递给链接器的 .obj 文件(或选项)中指定了内容。这是因为 [MS.Learn]: Building an Import Library and Export File (强调是我的):
Note that if you create your import library in a preliminary step, before creating your .dll, you must pass the same set of object files when building the .dll, as you passed when building the import library.
因此将 .obj 文件传递给链接器时会有所不同:
直接(命令行):包含在.dll(或.exe)
间接(通过命令行传递的 .lib 的一部分):它不包含在 .dll 中,它只搜索符号
这完全是有道理的,因为库只是 .obj 文件的集合(存档)(在 Nix 上,存档程序是 Ar(以前称为 RanLib))。一个例子:
输出:
[prompt]> del *.obj *.exp *.lib *.dll [prompt]> [prompt]> dir /b dll00.cpp lib00.cpp [prompt]> [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib00.obj lib00.cpp lib00.cpp [prompt]> [prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp dll00.cpp [prompt]> [prompt]> :: Pass lib00.obj directly to linker [prompt]> [prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.obj Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> lib /nologo /out:lib00.lib lib00.obj [prompt]> [prompt]> dir Volume in drive E is SSD0-WORK Volume Serial Number is AE9E-72AC Directory of e:\Work\Dev\StackOverflow\q056330888 23/01/30 09:15 <DIR> . 23/01/30 09:15 <DIR> .. 23/01/30 09:12 72 dll00.cpp 23/01/30 09:14 106,496 dll00.dll 23/01/30 09:14 733 dll00.exp 23/01/30 09:14 1,790 dll00.lib 23/01/30 09:14 604 dll00.obj 23/01/30 09:07 71 lib00.cpp 23/01/30 09:15 822 lib00.lib 23/01/30 09:13 604 lib00.obj 8 File(s) 111,192 bytes 2 Dir(s) 51,727,843,328 bytes free [prompt]> [prompt]> dumpbin /nologo /exports dll00.dll Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 00001010 foo Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text [prompt]> [prompt]> :: Now do the same with the one from inside the .lib [prompt]> [prompt]> del lib00.obj [prompt]> [prompt]> lib lib00.lib /extract:lib00.obj Microsoft (R) Library Manager Version 14.16.27048.0 Copyright (C) Microsoft Corporation. All rights reserved. [prompt]> [prompt]> dir lib00.obj Volume in drive E is SSD0-WORK Volume Serial Number is AE9E-72AC Directory of e:\Work\Dev\StackOverflow\q056330888 23/01/30 09:16 604 lib00.obj 1 File(s) 604 bytes 0 Dir(s) 51,727,839,232 bytes free [prompt]> [prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib00.obj Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00.dll Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 00001010 foo Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text
更新#1
我用 [MS.Learn]: Linker options 玩了一会儿(/INCLUDE 和/EXPORT)。增加了一些复杂性。
lib01.cpp:
//#pragma comment(linker, "/include:foo1") // Apparently, has no effect in an .obj contained by a .lib
#pragma comment(linker, "/export:foo01")
#if defined(__cplusplus)
extern "C" {
#endif
__declspec(dllexport) bool foo00()
{
return true;
}
bool foo01()
{
return true;
}
bool foo02()
{
return true;
}
#if defined(__cplusplus)
}
#endif
lib10.cpp:
#pragma comment(linker, "/export:foo11")
#if defined(__cplusplus)
extern "C" {
#endif
__declspec(dllexport) bool foo10()
{
return true;
}
bool foo11()
{
return true;
}
bool foo12()
{
return true;
}
#if defined(__cplusplus)
}
#endif
输出:
[prompt]> del *.obj *.exp *.lib *.dll [prompt]> [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib01.obj lib01.cpp lib01.cpp [prompt]> [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib10.obj lib10.cpp lib10.cpp [prompt]> [prompt]> lib /nologo /out:lib0110.lib lib01.obj lib10.obj [prompt]> [prompt]> cl /c /nologo /DDLL /Fodll00.obj dll00.cpp dll00.cpp [prompt]> [prompt]> :: ----- "Regular" behavior ----- [prompt]> [prompt]> link /nologo /dll /out:dll00.dll dll00.obj lib0110.lib Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00.dll Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001000 bar Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text [prompt]> [prompt]> :: ----- /export a symbol ----- [prompt]> [prompt]> link /nologo /dll /out:dll00_export.dll /export:foo02 dll00.obj lib0110.lib Creating library dll00_export.lib and object dll00_export.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00_export.dll Dump of file dll00_export.dll File Type: DLL Section contains the following exports for dll00_export.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 0000E1A0 foo02 Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text [prompt]> [prompt]> :: ----- /include a symbol ----- [prompt]> [prompt]> link /nologo /dll /out:dll00_include.dll /include:foo02 dll00.obj lib0110.lib Creating library dll00_include.lib and object dll00_include.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00_include.dll Dump of file dll00_include.dll File Type: DLL Section contains the following exports for dll00_include.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 3 number of functions 3 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 00001010 foo00 3 2 00001020 foo01 Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text
如所见(就像在文档中一样):
/EXPORT:搜索(在 .lib 中)符号 (foo02)并简单地导出它
/INCLUDE:搜索(在 .lib 中)符号 (foo02) , 获取包含目标文件 (lib0.obj),并将其包含在 .dll 中:
- 因此,在 .obj 文件中标记为导出的其他 2 个符号(foo00、foo01)被导出
结论
深入了解后发现 [MS.Learn]: /WHOLEARCHIVE (Include All Library Object Files) 其中指出(强调 是我的):
The /WHOLEARCHIVE option forces the linker to include every object file from either a specified static library, or if no library is specified, from all static libraries specified to the LINK command.
...
The /WHOLEARCHIVE option was introduced in Visual Studio 2015 Update 2.
输出:
[prompt]> :: ----- YAY ----- /wholearchive ----- YAY ----- [prompt]> [prompt]> link /nologo /dll /out:dll00_wholearchive.dll /wholearchive:lib0110.lib dll00.obj lib0110.lib Creating library dll00_wholearchive.lib and object dll00_wholearchive.exp [prompt]> [prompt]> dumpbin /nologo /exports dll00_wholearchive.dll Dump of file dll00_wholearchive.dll File Type: DLL Section contains the following exports for dll00_wholearchive.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 5 number of functions 5 number of names ordinal hint RVA name 1 0 00001000 bar 2 1 00001040 foo00 3 2 00001050 foo01 4 3 00001010 foo10 5 4 00001020 foo11 Summary 2000 .data 1000 .gehcont 1000 .gxfg 1000 .pdata 9000 .rdata 1000 .reloc E000 .text
关于c++ - 在链接到动态库的静态库中导出符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56330888/