c++ - 在链接到动态库的静态库中导出符号

标签 c++ linker static-libraries dynamic-linking dllexport

我在 MSVC2017 中有以下场景:

  1. 带有函数 bool foo()
  2. 的静态库
  3. 一个链接到上面静态库的动态链接库
  4. 使用显式运行时链接加载动态链接库并通过 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 个符号(foo00foo01)被导出

结论

深入了解后发现 [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/

相关文章:

c - 共享库 : Why muliple versions of GLIBC_ in a single binary

c++ - '链接错误 : already defined' when using COM interface

java - native_init() 链接错误

c++ - 应用内存使 golom 序列变慢

c++ - 打印数组中的元素

c++ - 在抛出 'std::bad_alloc' what(): std::bad_alloc 的实例后终止调用

c++ - siginfo_t 尚未声明 : caused by inclusion of a thrift header

iphone - 如何确定 Mac OS X 上静态库 (.a) 的目标体系结构?

c++ - 在静态库中包含 STL

objective-c - 构建一个使用 cocoapods 的可分发静态库