TL;博士
如果库是通过可执行文件或其他共享库链接的,那么linux加载和链接共享库的方式有什么不同吗?
背景
假设我有一个共享库(例如libA.so
),其中包含一个带有静态std::map
的类和一组单例类。每个单例类都可以访问地图,并静态地将自己的实例添加到地图中。
有两种情况:
libA.so
)来读取全局映射中的所有已注册类。libA.so
)中使用共享库(libB.so
)并在可执行文件中使用这个新库。在这种情况下,libB.so
使用libA.so
中的地图为可执行文件提供一些功能(如外观)。问题
如果我在可执行文件(方案1)中使用(即链接)此共享库,则上述映射包含单例类的列表,但是,如果我在另一个共享库中使用此库并且然后在可执行文件(方案2)中使用新的,地图似乎是空的。
我似乎无法理解链接器在任何一种情况下如何处理共享库。
更新
事实证明libB.so
未正确链接到libA.so
,即使明确指示使用-lA
的{{1}}标记。即使我看不到g++
使用libA.so
,libB.so
或ldd
链接的pmap
,但在使用objdump
类时我没有遇到运行时错误。如果我使用libA.so
运行相同的命令,我可以看到列出所有所需的库。
答案 0 :(得分:4)
Drepper的论文How To Write a Shared Library是该主题的一个很好的参考,应该回答你的问题。
您应该确保在第二种情况下,库只链接一次(例如使用完全相同的路径)。请参阅ld-linux(8)并使用例如LD_DEBUG
等......
你也可以strace
执行你的行为来理解发生了什么。
您应该检查(使用pmap
或使用cat /proc/$ThePid/maps
)库只加载一次。
答案 1 :(得分:4)
我将描述一个可能产生您所看到的行为的场景。
另一种情况:
libB.so
的地图,则可以在任何对象有机会注册之前调用它。通常,不保证来自不同翻译单元的全局构造函数的执行顺序,当然也不是来自不同的共享库。
上面第一个问题的解决方案是为地图使用单例样式模式,以便在使用时初始化,而不是通过全局构造函数初始化。
TheMap & GlobalMap () {
static TheMap instance;
return instance;
}
上述第二个问题的解决方案是禁止从全局构造函数访问全局映射。也就是说,将libB.so
中的所有全局构造函数更改为在使用时初始化,而不是在全局构造函数中初始化。