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

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(使用前将#替换为@)

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

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • 对类 static constexpr 结构的未定义引用,g++ 与 clang

    这是我的代码 a cp p struct int2 int x y struct Foo static constexpr int bar1 1 static constexpr int2 bar2 1 2 int foo1 return
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri

随机推荐