Exporting symbols in static library that is linked to dynamic library

╄→尐↘猪︶ㄣ 提交于 2020-04-16 02:51:46

问题


I have the following scenario in MSVC2017:

  1. A static library with the function bool foo()
  2. A dynamic link library that links to the static library above
  3. An application that loads the dynamic link library using explicit run-time linking and calls foo() via GetProcAddress

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)
  • 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!