链接可执行文件中的共享库与另一个共享库
Linking a shared library in executable vs. another shared lib
tl;dr
如果共享库由可执行文件或其他共享库链接,Linux 加载和链接共享库的方式是否存在任何差异?
背景
假设我有一个共享库(例如 libA.so
) 包含一个具有静态std::map
的类和一组单例类。每个单例类都可以访问映射,并以静态方式将自身的一个实例添加到映射中。
有两种情况:
- 我在可执行文件中使用共享库(
libA.so
)从全局映射中读取所有注册的类。
我在另一个共享库( libB.so
)中使用共享库(libA.so
),并在可执行文件中使用这个新库。在这种情况下,libB.so
使用来自libA.so
的映射为可执行文件提供一些功能(如外观)。
问题
如果我在可执行文件(场景 1)中使用(即链接)这个共享库,上述映射包含单例类的列表,但是,如果我在另一个共享库中使用此库,然后在可执行文件中使用新库(场景 2),映射似乎是空的。
我似乎无法理解链接器在这两种情况下如何处理共享库。
更新
事实证明,即使使用g++
标志明确指示-lA
libB.so
也没有正确链接到libA.so
。即使我看不到libA.so
使用 ldd
、pmap
或 objdump
libB.so
链接,但在使用 libA.so
类时我没有收到运行时错误。如果我使用 clang++
运行相同的命令,我可以看到列出了所有必需的库。
我将描述一个可能产生您所看到的行为的场景。
- 调用单一实例对象的全局构造函数,并根据映射注册它们。
- 调用映射的全局构造函数,导致映射初始化为空数据结构。
另一种情况:
- 如果您正在从全局构造函数
libB.so
读取映射,则可能会在任何对象有机会注册自身之前调用它。
通常,没有保证来自不同翻译单元的全局构造函数的执行顺序,当然也不是来自不同共享库的执行顺序。
上述第一个问题的解决方案是对地图使用单例样式模式,以便在使用时对其进行初始化,而不是通过全局构造函数进行初始化。
TheMap & GlobalMap () {
static TheMap instance;
return instance;
}
上述第二个问题的解决方案是禁止从全局构造函数访问全局映射。也就是说,将libB.so
中的所有全局构造函数更改为使用时初始化,而不是在全局构造函数中初始化。
Drepper的论文《如何编写共享库》是关于这个主题的一个很好的参考,应该可以回答你的问题。
您应该确保在第二种情况下,库仅链接一次(例如,使用完全相同的路径)。参见 ld-linux(8) 并使用例如 LD_DEBUG
等...
您还可以strace
执行以了解正在发生的事情。
并且您应该检查(使用 pmap
或使用 cat /proc/$ThePid/maps
)库是否只加载一次。
- 检查 2D 网格的某个元素是否与另一个元素共享对角线、水平线或垂直线
- 如何创建一个共享对象与另一个.所以在Cmake
- 提升线程问题,当一个线程与另一个线程没有相同的副本时如何共享变量?
- 将其转换为共享指针,并将其作为参数传递给另一个C++类
- 我可以有一个与另一个向量共享存储的向量
- 如何通过另一个类将共享指针传递给一个类?
- 如何将所有权从一个共享指针向量转移到另一个向量?
- 呼叫获取后,将共享_ptr分配给另一个
- 构建并使用使用另一个共享库的源文件中的共享库.(Rinside)
- 如何根据目标体系结构将共享库链接到另一个共享库
- 如何读取STD ::队列与另一个线程共享
- 如何知道一个共享库是否取决于另一个共享库
- 如何将共享库嵌入到另一个共享库中
- 共享内存空间可以将数据(非POD)发送到另一个共享内存吗?
- 与另一个类 c++ 共享对象
- 链接到链接到另一个共享库的共享库会在退出时出错
- 将共享库链接到另一个共享库
- 如何在 c++ 中与另一个类共享变量
- 将共享库与 Linux 中的另一个共享库链接
- 使用共享库的后果dlopen另一个具有不同构建类型的共享库