static constructors and symbol bindings in ld.so (2)

Rod Evans suggested me NOT to use "-Bdirect" for a C++ library. And thanks a lot to Stephen Clamage (ANSI C++ chairman), taught me to use "-instlib" option of CC. 

$ CC -flags
... ...
-instlib=<library>    Inhibit generation of instances already in <library>
... ...

The option causes the the compiler to scan the named library for template instances (and inline functions generated out of line), then omit generating them in the current .o file. Therefore, by specifying -instlib=./libbase.so when building libtest.so, the instantiated template "std::basic_string<unsigned int> str" would not be linked into libtest.so. So that the cyclic dependencies would not happen.

I added this option in the Makefile.am of im-scim.so, and it worked as expected.

-im_scim_la_CXXFLAGS=@GTK2_CFLAGS@
+im_scim_la_CXXFLAGS=@GTK2_CFLAGS@ \
+                   -instlib=$(top_builddir)/src/.libs/libscim-1.0.so

You could refer to [osol-tools-linking] thread for details.

static constructors and symbol bindings in ld.so

libbase.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <string>

class Bar
{
public:
        std::basic_string<unsigned int> name;
        char *buf;
        Bar ()  { buf = new char[10]; buf[9] = '';}
        ~Bar () { delete [] buf; }

};

static Bar bar;

void __initialize_libbase ()
{
        printf ("%s\n", bar.buf);
}

libtest.cpp: 

#include <string>
#include "libbase.h"

class Foo {
public:
        std::basic_string<unsigned int> str;
        Foo () { __initialize_libbase (); }
        void test () { printf ("test ()!\n"); }
};

static Foo foo;

test.c:

#include <dlfcn.h>
#include <link.h>

int main (int argc, char **argv)
{
        dlopen ("./libtest.so", RTLD_LAZY|RTLD_GLOBAL);
}

When the main program call dlopen ("libtest.so"), it resolves the dependencies, then adds libbase.so in the initialization sequence ahead of libtest.so (the order is reversed). And the static constructors in a shared library are in the .init routine.

In our case, while it's trying to initialize the static object "bar", it finds there is a symbol "xxx::__null_string_ref_rep<xxx>" (introduced by std::basic_string<unsigned int>), then looks up this symbol in loaded libraries. Firstly, it looks up the symbol in main program, then in libc.so, then finds matched one in libtest.so, and stops to move on (actually, libbase.so also has this symbol). Then it tries to initialize libtest.so, and initializes the static object "foo". Unfortunately, the constructor of Foo calls a external function in libbase, and this function accesses the static instance "bar", which is not initialized yet (the "buf" is not allocated).

So, it cores. That's the root cause why Scim GtkIMModule makes applications core.

While, if you added a main() in libtest.cpp, and compile it to an executable program, this problem would not happen. If you changed the flag from RTLD_LAZY to RTLD_NOW, this problem would not happen either. To resolve this, add -Bdirect option when you link the library. Refer to the new "Direct Binding" chapter of "Linker and Libraries guide".

And I need thank Rod Evans, he taught me to set LD_DEBUG env variable to show the debug informations. You could refer to [osol-tools-linking] thread for details.