Tuesday, May 6. 2008

Lost static objects in static libraries with GNU linker `ld`

If you link a static library to your executable, the GNU linker ld (as many others) only adds those symbols that ld thinks are actually used in your executable (that's a feature). Unfortunately, ld can only check if the symbol is in use, but not if the side effects of the symbol's initialization is used. Thus, your program will fail if it depends on the initialization of an unused static object.

In my case, I have a factory (class module::factory<T> : public factory_plant {...} factory;) at which classes can register themselves with a name (module::factory<Group> f("Group")). If I call the factory with a name (factory.create("Group")), the factory returns a pointer to a newly created class associated with that name. The class registers itself via a static object (here: f) whose initialization code does the actual registration.

The problem arises if the class is not used directly and the linker throws the static object away. The registration does not happen and thus I can't use the factory to create an object of the class. There are several possibilities to work around this feature:

Use --whole-archive to tell the linker to include all symbols, no matter what (with gcc use -Wl,--whole-archive -lmylib -Wl,--no-whole-archive). The drawback is that symbols that I neither use nor care about are also included, resulting in increased code size.

Use -u<symbol> to tell the linker to treat <symbol> as if undefined and thus include it in the executable, even it is not used. The drawback here is to know what <symbol> looks like. Easy for C files, difficult (and not portable) for C++ files (see Name mangling in Wikipedia).

Use static initializers (thanks to Alex for this). This is my preferred method. I use a throw-away class, which I add to the header file:

static struct Init {
     Init() {
          static module::factory<Group> init("Group");
     }
} init;