链接可执行文件中的共享库与另一个共享库

时间:2015-06-19 17:00:53

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

TL;博士

如果库是通过可执行文件或其他共享库链接的,那么linux加载和链接共享库的方式有什么不同吗?

背景

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

有两种情况:

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

    如果我在可执行文件(方案1)中使用(即链接)此共享库,则上述映射包含单例类的列表,但是,如果我在另一个共享库中使用此库并且然后在可执行文件(方案2)中使用新的,地图似乎是空的。

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

    更新

    事实证明libB.so未正确链接到libA.so,即使明确指示使用-lA的{​​{1}}标记。即使我看不到g++使用libA.solibB.soldd链接的pmap,但在使用objdump类时我没有遇到运行时错误。如果我使用libA.so运行相同的命令,我可以看到列出所有所需的库。

2 个答案:

答案 0 :(得分:4)

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

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

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

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

答案 1 :(得分:4)

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

  1. 调用单例对象的全局构造函数,并根据映射进行注册。
  2. 调用地图的全局构造函数,使地图初始化为空数据结构。
  3. 另一种情况:

    • 如果您正在从全局构造函数中读取libB.so的地图,则可以在任何对象有机会注册之前调用它。

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

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

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

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