From: Brian Cole on
I have two libraries, A and B. B includes A.h which has a Meyers
singleton implementation like the following:
class ClassA
{
protected:
static const ClassA &GetInstance()
{
static ClassA singleton;
return singleton;
}
};

I then compile the libraries with the following commands:
g++ -fPIC -c A.cpp
g++ -shared -o libA.so A.o
g++ -fPIC -c B.cpp
g++ -shared -o libB.so B.o -L. -lA

Now why does libB.so have a reference to ClassA::GetInstance when it
was told at link time (the last command) to use libA.so?
coleb(a)vader~/tests$ ldd libB.so | grep libA
libA.so => ./libA.so (0x00002ade269c1000)
coleb(a)vader~/tests$ nm libB.so | grep GetInstance
0000000000000690 W _ZN6ClassA11GetInstanceEv
00000000002009dc V _ZZN6ClassA11GetInstanceEvE9singleton
coleb(a)vader~/tests$ nm libA.so | grep GetInstance
0000000000000680 W _ZN6ClassA11GetInstanceEv
00000000002009bc V _ZZN6ClassA11GetInstanceEvE9singleton

This is causing me problems when I load two separate shared libraries
into Python. Python dlopen's shared libraries with RTLD_LOCAL (I'm
guessing because it assumes the worst: name conflicts can occur
between two unrelated libraries).

Python will load libA.so and libB.so. The libA.so singleton will be
used when libA is used normally from Python. But when a call is made
into libB.so which subsequently makes a call into libA.so a different
singleton is returned, violating the singleton semantics. The
singleton is actually switching midway through a function call in
libB.so.

Is there a good reason dlopen("libB.so") does not cause the libA.so
ClassA::GetInstance symbol to be used within libB.so? Especially since
libB.so was explicitly linked against libA.so.

Couple notes:
1) I realize moving the GetInstance implementation into a .cpp file
that is compiled into a .o relieves the problem since B.o no longer
needs to compile its own version. However, this is not feasible if
ClassA is a template.
2) I know making python dlopen with the RTLD_GLOBAL flag solves the
problem. But how is this a desirable solution as it requires the
library user to know about implementation details of libB?

-Brian
From: Giorgos Keramidas on
On Mon, 21 Jul 2008 18:59:59 -0700 (PDT), Brian Cole <coleb2(a)gmail.com> wrote:
> I have two libraries, A and B. B includes A.h which has a Meyers
> singleton implementation like the following:
[...]
> Python will load libA.so and libB.so. The libA.so singleton will be
> used when libA is used normally from Python. But when a call is made
> into libB.so which subsequently makes a call into libA.so a different
> singleton is returned, violating the singleton semantics. The
> singleton is actually switching midway through a function call in
> libB.so.
>
> Is there a good reason dlopen("libB.so") does not cause the libA.so
> ClassA::GetInstance symbol to be used within libB.so? Especially since
> libB.so was explicitly linked against libA.so.
>
> Couple notes:
> 1) I realize moving the GetInstance implementation into a .cpp file
> that is compiled into a .o relieves the problem since B.o no longer
> needs to compile its own version. However, this is not feasible if
> ClassA is a template.
> 2) I know making python dlopen with the RTLD_GLOBAL flag solves the
> problem. But how is this a desirable solution as it requires the
> library user to know about implementation details of libB?

RTLD_GLOBAL is a pretty heavy axe to grind in this case. Maybe you can
hack around this by a 'libmyloader.so' that runs dlopen(RTLD_LOCAL) on
both libA.so and libB.so?

From: Paul Pluzhnikov on
Brian Cole <coleb2(a)gmail.com> writes:

> Now why does libB.so have a reference to ClassA::GetInstance when it
> was told at link time (the last command) to use libA.so?

On ELF platforms, gcc by default instantiates every inline method
as a weak symbol in every compilation unit in which that symbol is
visible to gcc. The linker then discards duplicates from multiple
objects, while linking a shared library or an executable.

AFAIK, the linker pays no attention to the fact that the same
symbol is already defined in a shared library included later on
the link line.

Without RTLD_LOCAL, it all works as one would expect. With
RTLD_LOCAL, you'd better know what you are doing :(

Some other possible solutions:
A. merge libA.so and libB.so into a combined library.
B. if libB.so doesn't actually use ClassA::GetInstance(),
compile it with -ffunction-sections -fdata-sections and link
with -Wl,--gc-sections,-O

Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.