简体   繁体   English

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

[英]Linking a shared library in executable vs. another shared lib

tl;dr tl; dr

Are there any differences in the way linux loads and links a shared library if the library is linked by an executable or by another shared library? 如果库是通过可执行文件或其他共享库链接的,则linux加载和链接共享库的方式是否有所不同?

Background 背景

Suppose I have a shared library (eg libA.so ) containing a class with a static std::map and a set of singleton classes. 假设我有一个共享库(例如libA.so ),其中包含一个带有静态std::map和一组单例类。 Each singleton class has access to the map and statically adds an instance of itself to the map. 每个单例类都可以访问地图,并将自身的实例静态添加到地图。

There are two scenarios: 有两种情况:

  1. I use the shared library ( libA.so ) in an executable to read all the registered classes from the global map. 我在可执行文件中使用共享库( libA.so )从全局映射中读取所有已注册的类。
  2. I use the shared library ( libA.so ) in another shared library ( libB.so ) and use this new one in an executable. 我在另一个共享库( libB.so )中使用共享库( libA.so ),并在可执行文件中使用这个新库。 In this case libB.so uses the map from libA.so to provide some functionality to the executable (like a facade). 在这种情况下, libB.so使用libA.so的映射为可执行文件提供某些功能(例如外观)。

Problem 问题

If I use (ie link) this shared library in an executable (scenario 1), the aforementioned map contains the list of singleton classes, however , if I use this library in another shared library and then use the new one in an executable (scenario 2), the map seems to be empty. 如果我在可执行文件中使用(即链接)此共享库(场景1),则上述映射包含单例类的列表, 但是 ,如果我在另一个共享库中使用此库,然后在可执行文件中使用新的共享库(场景1) 2),地图似乎是空的。

I cannot seem to understand how the linker handles the shared libraries in either of cases. 在这两种情况下,我似乎都无法理解链接器如何处理共享库。

Update 更新资料

As it turned out libB.so does not link to libA.so correctly even as explicitly instructed using -lA flag of g++ . 因为它变成了libB.so不链接到libA.so正确,即使使用的明确指示-lA的标志g++ Even though I cannot see libA.so linked by libB.so using ldd , pmap or objdump , I get no runtime errors when using classes of libA.so . 虽然我不能看到libA.so通过链接libB.so使用lddpmapobjdump ,我使用的类时,没有得到运行时错误libA.so If I run the same command with clang++ I can see that all required libraries are listed. 如果使用clang++运行相同的命令,则可以看到列出了所有必需的库。

Drepper's paper How To Write a Shared Library is a good reference on the subject and should answer your question. Drepper的论文“ 如何编写共享库”是该主题的很好参考,应该回答您的问题。

You should be sure that in the second scenario, the library is linked only once (eg using the exact same path). 您应该确保在第二种情况下,库仅链接一次(例如,使用完全相同的路径)。 See ld-linux(8) and use eg LD_DEBUG etc... 参见ld-linux(8)并使用例如LD_DEBUG等...

You might also strace your execution to understand what is going on. 您可能还会strace执行以了解发生了什么。

And you should check (with pmap or using cat /proc/$ThePid/maps ) that the library is loaded only once. 并且您应该检查(使用pmap或使用cat /proc/$ThePid/maps )该库仅加载一次。

I will describe a scenario that could produce the behavior you are seeing. 我将描述一种可能产生您所看到的行为的方案。

  1. Global constructors of the singleton objects are called, and they get registered against the map. 调用单例对象的全局构造函数,并针对地图进行注册。
  2. Global constructor of the map is called, causing the map to be initialized into an empty data structure. 映射的全局构造函数被调用,从而导致将映射初始化为空的数据结构。

Another scenario: 另一种情况:

  • If you are reading the map from libB.so from a global constructor, it may be called before any object has had a chance to register itself. 如果您正在从全局构造函数的libB.so中读取映射,则可能在任何对象有机会注册自身之前调用它。

Generally, there is no guaranteed order of execution of global constructors from different translation units, and certainly not from different shared libraries. 通常,不能保证从不同的翻译单元(当然也不能从不同的共享库)执行全局构造函数的顺序。

A solution to the first issue above would be to use a singleton style pattern for your map, so that it is initialized on use rather than through a global constructor. 上面第一个问题的解决方案是为地图使用单例样式模式,以便在使用时进行初始化,而不是通过全局构造函数进行初始化。

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

A solution to the second issue above would be suppress accessing the global map from global constructors. 上面第二个问题的解决方案是禁止从全局构造函数访问全局地图。 That is, change all global constructors in libB.so to be initialize on use rather than initialize in a global constructor. 也就是说,将libB.so所有全局构造函数更改为在使用时进行初始化,而不是在全局构造函数中进行初始化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM