【问题标题】:Linking a shared library in executable vs. another shared lib链接可执行文件中的共享库与另一个共享库
【发布时间】:2015-09-05 18:04:43
【问题描述】:

tl;dr

如果库是由可执行文件或另一个共享库链接的,linux 加载和链接共享库的方式有什么不同吗?

背景

假设我有一个共享库(例如libA.so),其中包含一个带有静态std::map 的类和一组单例类。每个单例类都可以访问地图并将其自身的实例静态添加到地图中。

有两种情况:

  1. 我在可执行文件中使用共享库 (libA.so) 从全局映射中读取所有已注册的类。
  2. 我在另一个共享库 (libB.so) 中使用共享库 (libA.so),并在可执行文件中使用这个新库。在这种情况下,libB.so 使用来自libA.so 的映射为可执行文件提供一些功能(如外观)。

问题

如果我在可执行文件中使用(即链接)这个共享库(场景 1),如果我在另一个共享库中使用这个库并且然后在可执行文件中使用新的(场景2),地图似乎是空的。

我似乎无法理解链接器在这两种情况下如何处理共享库。

更新

事实证明,libB.so 没有正确链接到 libA.so,即使使用 g++-lA 标志明确指示也是如此。即使我看不到libB.so 使用lddpmapobjdump 链接的libA.so,但在使用libA.so 的类时我没有遇到运行时错误。如果我使用 clang++ 运行相同的命令,我可以看到列出了 所有 所需的库。

【问题讨论】:

  • 在 UNIX 系统上,共享库仅为其代码页共享内存。除非您编写特殊代码使库使用共享内存,否则数据页仍与使用库的进程一起驻留。我相信 Windows 有一种 DLL 将共享数据和代码的模式。
  • @jxh 我在 Linux 上,但仍然无法理解当我通过另一个共享库链接共享库时所有这些静态初始化会发生什么,以及为什么如果通过可执行文件直接链接它就可以正常工作?
  • 当您生成第二个库时,您是否 100% 确定您对第一个共享库进行了静态链接?
  • @jxh 我更新了这个问题,希望现在更清楚了。
  • @eugeniu-rosca 我 100% 确定我不会静态链接它,因为我的库应该被其他模块使用。

标签: c++ linux linker g++ clang++


【解决方案1】:

我将描述一个可能产生您所看到的行为的场景。

  1. 调用单例对象的全局构造函数,并将它们注册到映射中。
  2. 地图的全局构造函数被调用,导致地图被初始化为一个空的数据结构。

另一种情况:

  • 如果您从全局构造函数中读取来自libB.so 的映射,则它可能会在任何对象有机会注册自己之前被调用。

通常,来自不同翻译单元的全局构造函数的执行顺序没有保证,当然也不能来自不同的共享库。

上述第一个问题的解决方案是为您的地图使用单例样式模式,以便在使用时对其进行初始化,而不是通过全局构造函数进行初始化。

TheMap & GlobalMap () {
    static TheMap instance;
    return instance;
}

解决上述第二个问题的方法是禁止从全局构造函数访问全局映射。也就是说,将libB.so 中的所有全局构造函数更改为在使用时进行初始化,而不是在全局构造函数中进行初始化。

【讨论】:

  • 我认为全局构造函数的概念正是我所缺少的。我更新了这个问题只是为了澄清我到底在做什么。我会说你的回答只是解释了我陷入的情况。
  • 我认为在场景 2 后期执行的地图的全局构造函数是导致您的问题的原因,正如我所说,没有保证全局构造函数的执行顺序。您可能可以添加一些打印语句来证明在构建地图之前正在发生注册。至于为什么被另一个库链接时顺序会发生变化,这可能与链接器构建的一些对象依赖树有关,但我没有明确的答案。
  • 我刚刚检查了LD_DEBUG=libs,发现在可执行文件的情况下,我有这行calling init: /PATH/TO/LIB/libA.so,如果我在第二种情况下尝试调试,就会丢失。所以你是完全正确的!
【解决方案2】:

Drepper 的论文 How To Write a Shared Library 是该主题的一个很好的参考,应该可以回答您的问题。

您应该确保在第二种情况下,该库仅链接一次(例如,使用完全相同的路径)。请参阅ld-linux(8) 并使用例如LD_DEBUG等等...

你也可以strace你的执行以了解发生了什么。

您应该检查(使用pmap 或使用cat /proc/$ThePid/maps)该库仅加载一次。

【讨论】:

  • 请编辑无法访问的链接。 +1 提及pmap
  • @BasileStarynkevitch 事实证明,当使用lddobjdump -p 时,库根本没有被初始化,甚至不会被视为需要DSO。 但是只有在使用g++ 时才会发生这种情况。使用clang,所有必需的库都已正确链接。
猜你喜欢
  • 2013-10-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 2016-01-02
  • 2011-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多