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?
Background
Suppose I have a shared library (e.g. libA.so
) containing a class with a static std::map
and a set of singleton classes. Each singleton class has access to the map and statically adds an instance of itself to the map.
There are two scenarios:
- I use the shared library (
libA.so
) in an executable to read all the registered classes from the global map. - I use the shared library (
libA.so
) in another shared library (libB.so
) and use this new one in an executable. In this caselibB.so
uses the map fromlibA.so
to provide some functionality to the executable (like a facade).
Problem
If I use (i.e. 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.
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++
. 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
. If I run the same command with clang++
I can see that all required libraries are listed.
I will describe a scenario that could produce the behavior you are seeing.
Another scenario:
libB.so
from a global constructor, it may be called before any object has had a chance to register itself.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.
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.