问题
I have the following scenario in MSVC2017:
- A static library with the function
bool foo()
- A dynamic link library that links to the static library above
- An application that loads the dynamic link library using explicit run-time linking and calls
foo()
viaGetProcAddress
In the static library, foo()
is defined as follows:
extern "C" __declspec(dllexport) bool foo()
{
return true;
}
Now, because foo()
is not used by the dynamic link library, its symbol is not exported and thus not findable when the application used GetProcAddress
.
I have tried:
#pragma comment(linker, "/include:foo")
and:
#pragma comment(linker, "/export:foo")
I can see the exported foo()
using Dependency Walker if I move the definition to the dynamic link library (not a viable solution) but I cannot seem to get the symbol exported when I keep the definition in the static library with the above linker switches. I presume this is because the symbol is still not used, and thus still not exported regardless?
I would like a solution for both MSVC on Windows and Clang on Linux. Thanks!
回答1:
My solution in the end was to make a dummy function that called foo()
to force all symbols in that compilation unit to be exported.
回答2:
You're doing something wrong (or at least not as you describe in the question). Of course, what you posted in your answer works as well, but that's only a workaround, as the "regular" way should work.
Here's a small example.
lib.cpp:
extern "C" __declspec(dllexport) bool foo() {
return true;
}
dll.cpp:
extern "C" __declspec(dllexport) bool bar() {
return false;
}
Output:
[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 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.13 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' [prompt]> dir /b dll.cpp lib.cpp [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib.obj lib.cpp lib.cpp [prompt]> lib /nologo /out:lib.lib lib.obj [prompt]> [prompt]> cl /c /nologo /DDLL /Fodll.obj dll.cpp dll.cpp [prompt]> link /nologo /dll /out:dll.dll dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dir /b dll.cpp dll.dll dll.exp dll.lib dll.obj lib.cpp lib.lib lib.obj [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text [prompt]> [prompt]> :: ----- Re-link dll, instructing it to include foo ----- [prompt]> [prompt]> link /nologo /dll /include:foo /out:dll.dll dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text
Notes:
- As noticed, I used command line, but the same commands (more arguments) are invoked by VStudio IDE
- Adding /include:foo (2ndlink command) exports foo as well (as seen in the next dumpbin output):
- Specifying this option is identical to adding
#pragma comment(linker, "/include:foo")
(in dll.cpp - or any file that is being directly passed to the linker) - /export:foo is not necessary, as the function is already exported by __declspec(dllexport)
- Specifying this option is identical to adding
- I didn't go through the end (the application), since foo being present in dumpbin output is enough (it's also visible from Dependency Walker)
Update #0
You might not be doing things wrong after all. But bear in mind that it's not scalable (if you have hundreds of such symbols). Looking at [MS.Docs]: Overview of LIB, it provides the same options as link in regards to exporting stuff. But they seem to be ignored.
When building a lib, maybe one would like to specify all the symbols to be included at link time (either via option or via #pragma comment), when building the .lib, and not when linking. Apparently, they are ignored (I've tested it), unless stuff is specified in .obj files (or options) passed directly to the linker. This is because [MS.Docs]: Building an Import Library and Export File (emphasis is mine):
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.
So there's a difference when passing an .obj file to the linker:
- Directly (command line): it is included in the .dll (or .exe)
- Indirectly (part of a .lib passed via command line): it is not included in the .dll, it is only searched for symbols
This totally makes sense as a lib is just a collection (archive) of .obj files (on Nix the archiver is ar (formerly known as ranlib)). An example:
Output:
[prompt]> del *.obj *.exp *.lib *.dll [prompt]> dir /b dll.cpp lib.cpp [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib.obj lib.cpp lib.cpp [prompt]> cl /c /nologo /DDLL /Fodll.obj dll.cpp dll.cpp [prompt]> :: Pass lib.obj directly to linker [prompt]> link /nologo /dll /out:dll.dll dll.obj lib.obj Creating library dll.lib and object dll.exp [prompt]> lib /nologo /out:lib.lib lib.obj [prompt]> [prompt]> dir Volume in drive E is SSD0-WORK Volume Serial Number is AE9E-72AC Directory of e:\Work\Dev\StackOverflow\q056330888 20/04/08 14:28 <DIR> . 20/04/08 14:28 <DIR> .. 19/06/30 20:03 114 dll.cpp 20/04/08 14:27 88,576 dll.dll 20/04/08 14:27 729 dll.exp 20/04/08 14:27 1,764 dll.lib 20/04/08 14:27 604 dll.obj 20/04/08 14:04 68 lib.cpp 20/04/08 14:28 822 lib.lib 20/04/08 14:27 604 lib.obj 8 File(s) 93,281 bytes 2 Dir(s) 83,419,111,424 bytes free [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text [prompt]> :: Now do the same with the one from inside the .lib [prompt]> del lib.obj [prompt]> lib lib.lib /extract:lib.obj Microsoft (R) Library Manager Version 14.16.27038.0 Copyright (C) Microsoft Corporation. All rights reserved. [prompt]> dir lib.obj Volume in drive E is SSD0-WORK Volume Serial Number is AE9E-72AC Directory of e:\Work\Dev\StackOverflow\q056330888 20/04/08 14:28 604 lib.obj 1 File(s) 604 bytes 0 Dir(s) 83,419,107,328 bytes free [prompt]> link /nologo /dll /out:dll.dll dll.obj lib.obj Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .tex
Update #1
I played briefly with [MS.Docs]: Linker options (/INCLUDE and /EXPORT). Added a bit of complexity into the mix.
lib0.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
lib1.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
Output:
[prompt]> del *.obj *.exp *.lib *.dll [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib0.obj lib0.cpp lib0.cpp [prompt]> cl /c /nologo /D_LIB /DSTATIC /Folib1.obj lib1.cpp lib1.cpp [prompt]> lib /nologo /out:lib.lib lib0.obj lib1.obj [prompt]> cl /c /nologo /DDLL /Fodll.obj dll.cpp dll.cpp [prompt]> :: ----- "Regular" behavior ----- [prompt]> link /nologo /dll /out:dll.dll dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text [prompt]> [prompt]> :: ----- /export a symbol ----- [prompt]> link /nologo /dll /out:dll.dll /export:foo02 dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 0000BB60 foo02 Summary 2000 .data 1000 .pdata 9000 .rdata 1000 .reloc B000 .text [prompt]> [prompt]> :: ----- /include a symbol ----- [prompt]> link /nologo /dll /out:dll.dll /include:foo02 dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text
As seen (just like in the docs):
- /EXPORT: searches (in the .lib) for the symbol (foo02) and simply exports it
- /INCLUDE: searches (in the .lib) for the symbol (foo02), gets the containing object file (lib0.obj), and includes it in the .dll:
- As a consequence, the other 2 symbols (foo00, foo01) marked for export in the .obj file are exported
Conclusion
Took a deeper look and found [MS.Docs]: /WHOLEARCHIVE (Include All Library Object Files) which states (emphasis is mine):
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.
Output:
[prompt]> :: ----- YAY ----- /wholearchive ----- YAY ----- [prompt]> link /nologo /dll /out:dll.dll /wholearchive:lib.lib dll.obj lib.lib Creating library dll.lib and object dll.exp [prompt]> dumpbin /nologo /exports dll.dll Dump of file dll.dll File Type: DLL Section contains the following exports for dll.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 .pdata 9000 .rdata 1000 .reloc B000 .text
来源:https://stackoverflow.com/questions/56330888/exporting-symbols-in-static-library-that-is-linked-to-dynamic-library