Multiple instances of singleton across shared libraries on Linux

First, you should generally use -fPIC flag when building shared libraries.

Not using it “works” on 32-bit Linux, but would fail on 64-bit one with an error similar to:

/usr/bin/ld: /tmp/ccUUrz9c.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC

Second, your program will work as you expect after you add -rdynamic to the link line for the main executable:

singleton.num in main : 100
singleton.num in hello.so : 100
singleton.num in hello.so after ++ : 101
singleton.num in main : 101

In order to understand why -rdynamic is required, you need to know about the way dynamic linker resolves symbols, and about the dynamic symbol table.

First, let’s look at the dynamic symbol table for hello.so:

$ nm -C -D hello.so | grep singleton
0000000000000b8c W singleton::instance()
0000000000201068 B singleton::pInstance
0000000000000b78 W singleton::singleton()

This tells us that there are two weak function definitions, and one global variable singleton::pInstance that are visible to the dynamic linker.

Now let’s look at the static and dynamic symbol table for the original example1 (linked without -rdynamic):

$ nm -C  example1 | grep singleton
0000000000400d0f t global constructors keyed to singleton::pInstance
0000000000400d38 W singleton::instance()
00000000006022e0 B singleton::pInstance
0000000000400d24 W singleton::singleton()

$ nm -C -D example1 | grep singleton
$ 

That’s right: even though the singleton::pInstance is present in the executable as a global variable, that symbol is not present in the dynamic symbol table, and therefore “invisible” to the dynamic linker.

Because the dynamic linker “doesn’t know” that example1 already contains a definition of singleton::pInstance, it doesn’t bind that variable inside hello.so to the existing definition (which is what you really want).

When we add -rdynamic to the link line:

$ nm -C  example1-rdynamic | grep singleton
0000000000400fdf t global constructors keyed to singleton::pInstance
0000000000401008 W singleton::instance()
00000000006022e0 B singleton::pInstance
0000000000400ff4 W singleton::singleton()

$ nm -C -D  example1-rdynamic | grep singleton
0000000000401008 W singleton::instance()
00000000006022e0 B singleton::pInstance
0000000000400ff4 W singleton::singleton()

Now the definition of singleton::pInstance inside the main executable is visible to the dynamic linker, and so it will “reuse” that definition when loading hello.so:

LD_DEBUG=bindings ./example1-rdynamic |& grep pInstance
     31972: binding file ./hello.so [0] to ./example1-rdynamic [0]: normal symbol `_ZN9singleton9pInstanceE'

Leave a Comment