链接共享库可执行与另一个共享库
TL;博士链接共享库可执行与另一个共享库
是否有这样的Linux负载的任何分歧和链接共享库,如果库是由可执行文件或通过其他共享库链接?
背景
假设我有包含具有静态std::map
一个类共享库(例如libA.so
)和一组单类。每个单身人士课程都可以访问地图并静态地将自己的一个实例添加到地图中。
有两种情况:
- 我使用共享库(
libA.so
)中的可执行文件读取所有注册类从全球地图。 - 我使用共享库(
libA.so
)在另一个共享库(libB.so
),并使用在可执行这个新的。在这种情况下,libB.so
使用来自libA.so
的地图为可执行文件提供一些功能(如外观)。
问题
如果使用(即链接)此共享库中的可执行(方案1)中,上述地图包含单类的列表,然而,如果使用这个库在另一个共享库中,然后在可执行文件中使用新文件(场景2),地图似乎是空的。
我似乎无法理解链接器如何处理共享库,在任何情况下。
更新
事实证明libB.so
不链接到正确libA.so
即使使用的g++
-lA
标志作为明确指示。虽然我不能看到libA.so
通过libB.so
使用ldd
,pmap
或objdump
联系,我使用的libA.so
类时,没有得到运行时错误。如果我运行与clang++
相同的命令,我可以看到所有所需的库都列出。
我将描述一个可能产生您所看到的行为的场景。
- 单体对象的全局构造函数被调用,并且它们会根据地图进行注册。
- 地图的全局构造函数被调用,导致地图被初始化为一个空的数据结构。
另一种情形:
- 如果你正在阅读从
libB.so
从全球构造的地图,它可以在任何物体都有机会进行自我注册之前调用。
一般来说,没有保证从不同的翻译单元执行全局构造函数的顺序,当然也不是来自不同的共享库。
上述第一个问题的解决方案是为地图使用singleton样式模式,以便在使用时初始化它,而不是通过全局构造函数初始化。
TheMap & GlobalMap() {
static TheMap instance;
return instance;
}
上述第二个问题的解决方案将禁止从全局构造函数访问全局映射。也就是说,将libB.so
中的所有全局构造函数更改为初始化,而不是在全局构造函数中进行初始化。
我认为全球构造者的概念正是我所缺少的。我更新了这个问题,以澄清我到底做了什么。我想说,你的回答只是解释我陷入的局面。 –
我认为在场景2中执行得迟的地图的全局构造函数是导致你的问题的原因,正如我所说的,没有保证执行顺序全球建设者。您可以添加一些打印语句来证明在地图构建之前注册正在发生。至于为什么顺序在被另一个库链接时会发生变化,这可能与链接器构建的某个对象依赖关系树有关,但我没有确定的答案。 – jxh
我刚刚用'LD_DEBUG = libs'进行了检查,发现在可执行文件的情况下,我有这行'调用init:/ PATH/TO/LIB/libA.so',如果我在第二次尝试调试场景。所以你是完全正确的! –
Drepper的论文How To Write a Shared Library关于这个问题的一个很好的参考,并应回答你的问题。
你应该确保在第二个方案中,库被链接只有一次(例如,使用完全相同的路径)。见ld-linux(8)并使用例如LD_DEBUG
等等
您可能还strace
您的执行,了解正在发生的事情。
而且您应该检查(使用pmap
或使用cat /proc/$ThePid/maps
)该库只加载一次。
请编辑不可访问的链接。 +1提到'pmap'。 –
谢谢,更正了链接。 –
@BasileStarynkevitch事实证明,当使用'ldd'或'objdump -p'时,库不会被初始化,甚至不会被视为需要DSO。 **然而**这只有在使用'g ++'时才会发生。通过'clang',所有必需的库都能正确链接。 –
在UNIX系统上,共享库仅为其代码页共享内存。除非您编写特殊代码以使库使用共享内存,否则数据页仍驻留在使用库的进程中。我相信Windows有一个DLL将共享数据和代码的模式。 – jxh
@jxh我在Linux上,但仍然无法理解当我通过另一个共享库链接共享库时为什么会发生所有这些静态初始化,以及为什么它可以正常工作,如果直接通过可执行文件链接它? –
当你生成你的第二个库时,你是否100%确定你是第一个共享库的静态链接? –