Nat! bio photo

Nat!

Senior Mull

Twitter Github Twitch

Challenges of shared library environments, Part 1

This is a small article series concerned with static initialization and destruction.

In C, static initialization is platform and compiler dependent. For gcc and clang it comes in the form of:

__attribute__((constructor))
static void   load( void)
{
   printf( "Hey, I am running before main!\n");
}

When the compiled code containing such a function is loaded, the function is executed immediately or almost…

ELF loading is non-deterministic

In this article, we’ll take a look at ELF, the linux object file format, and how it is initializing.

Unrelated libraries are unpredictable

Assume you have two shared libraries a, b. Even though you linked the libraries in the order a, b against your executable ELF doesn’t guarantee - maybe in a desire to support parallelism - that they are loaded in this order. Consequentially if a and b have initializers, they will not execute in a predictable sequence.

ELF:

The order of processing among the entries of a particular
list of needed objects is unspecified.

LD_PRELOAD is unpredictable as well

There is a way to inject shared libraries using LD_PRELOAD. You might be interested to use LD_PRELOAD, if you have a library that wants to interpose some API before anyone else has a chance of calling that API. LD_PRELOAD doesn’t work reliably with respect to initializers though. Even though a is indeed pre-loaded before b any initializer of b can be executed before a.

Lets have a look at dependencies now. Assume a and b both link against c but not against each other. What is guaranteed by ELF is, that c will be loaded before a and b. It is also specified, that the c initializer runs before a and b. Consequently c’s destructor will run last. Still the order of b and a initialization remains undefined.

Again ELF:

Before the initialization code for any object A is
called, the initialization code for any other objects
that object A depends on are called. For these
purposes, an object A depends on another object B, if
B appears in A’s list of needed objects...

Conclusion

The bottom line is, if you need a specific initialization order without co-operation: you can’t do it unless you make libraries depend on each other (which is basically another form of co-operation).

LD_PRELOAD does not do as much as you’d expect.

Does this concern mulle-objc ?

For classes that behavior is uninteresting as long as the classes link against the shared library of the runtime. The mulle-objc runtime does the queuing and dependency management.

The problem for mulle-objc tests is, how to interpose mulle-testallocator so it is initialized before the mulle-objc-runtime.


Post a comment

All comments are held for moderation; basic HTML formatting accepted.

Name:
E-mail: (not published)
Website: