导出链接到动态库的静态库中的符号

2023-11-21

我在MSVC2017中有以下场景:

  1. 具有该功能的静态库bool foo()
  2. 链接上面静态库的动态链接库
  3. 使用显式运行时链接和调用加载动态链接库的应用程序foo() via GetProcAddress

在静态库中,foo()定义如下:

extern "C" __declspec(dllexport) bool foo() 
{
    return true;   
}

现在,因为foo()不被动态链接库使用,其符号未导出,因此应用程序使用时找不到GetProcAddress.

我努力了:

#pragma comment(linker, "/include:foo")

and:

#pragma comment(linker, "/export:foo")

我可以看到导出的foo()如果我将定义移动到动态链接库(不是可行的解决方案),则使用 Dependency Walker,但当我使用上述链接器开关将定义保留在静态库中时,我似乎无法导出符号。我认为这是因为该符号仍未使用,因此仍然没有导出?

我想要一个适用于 Windows 上的 MSVC 和 Linux 上的 Clang 的解决方案。谢谢!


你做错了什么(或者至少不像你在问题中描述的那样)。当然,您在答案中发布的内容也有效,但这只是一种解决方法,因为“常规”方式应该有效。
这是一个小例子。

库00.cpp:

extern "C" __declspec(dllexport) bool foo()
{
    return true;
}

dll00.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 > 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

Notes:

  • 如前所述,我使用了命令行,但相同的命令(更多参数)由VStudio IDE

  • Adding /include:foo (2nd Link command) exports foo as well (as seen in the next DumpBin output):

    • 指定此选项与添加相同#pragma comment(linker, "/include:foo") (in dll.cpp- 或直接传递给链接器的任何文件)

    • /导出:foo不是必需的,因为该函数已经由__declspec(dll导出)

  • 我没有完成最后(申请),因为foo存在于DumpBin输出就足够了(也可以从依赖步行者)



Update #0

毕竟你可能没有做错事。但请记住它不可扩展(如果您有数百个此类符号)。看着[MS.Learn]:LIB概述,它提供与Link关于出口东西。但他们似乎被忽视了。

当建造一个.lib,也许有人想指定链接时要包含的所有符号(通过选项或通过#pragma 评论), when 建设.lib,而不是在链接时。显然,它们被忽略(我已经测试过),除非在.obj文件(或选项)直接传递给链接器。这是因为[MS.Learn]:构建导入库和导出文件 (emphasis是我的):

请注意,如果您在创建 .dll 之前的预备步骤中创建导入库,构建 .dll 时必须传递与构建导入库时传递的同一组目标文件.

所以通过时有一个区别.obj文件到链接器:

  • 直接(命令行):它包含在.dll (or .exe)

  • 间接(一部分.lib通过命令行传递):它不包含在.dll,仅搜索符号

这完全有道理,因为库只是一个集合(存档).obj文件(在Nix归档器是Ar(原名RanLib))。一个例子:

Output:

[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


Update #1

我短暂地玩过[MS.Learn]:链接器选项 (/INCLUDE and /EXPORT)。混合中增加了一点复杂性。

库01.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

库10.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]>
[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

正如所见(就像在Docs):

  • /EXPORT:搜索(在.lib)对于符号(foo02)并简单地导出它

  • /INCLUDE:搜索(在.lib)对于符号(foo02),获取包含的目标文件(lib0.obj), and 将其包含在.dll:

    • 因此,另外 2 个符号 (foo00, foo01)标记为出口.obj文件已导出

结论

深入观察发现[MS.Learn]:/WHOLEARCHIVE(包括所有库对象文件)其中指出(emphasis是我的):

/WHOLEARCHIVE 选项强制链接器包含每个目标文件来自指定的静态库,或者如果未指定库,来自所有静态库指定给 LINK 命令。

...

/WHOLEARCHIVE 选项是在 Visual Studio 2015 Update 2 中引入的。

Output:

[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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

导出链接到动态库的静态库中的符号 的相关文章

随机推荐