arayq2
arayq2

Reputation: 2554

Are static c++ objects in dynamically loaded libraries initialized before dlopen() returns?

The code below demonstrates the expected (and arguably, intuitive) behavior. Just as static objects in an executable are initialized before main() is entered, one would expect static objects in a dynamically loaded library to be initialized before dlopen() returns.

The issue: is this behavior for libraries loaded at runtime guaranteed in any way, or is it just a convenient accident or a lucky implementation detail? Can we rely on constructors of static objects in the libraries being invoked, or do we have to resort to alternatives such as functions marked with __attribute__((constructor)) in order to ensure some desired behavior within the scope of the dlopen() call?

// libtest.cpp
#include <iostream>

namespace
{
    class Test
    {
    public:
        Test()  { std::cerr << "In Test()...\n"; }
        ~Test() { std::cerr << "In ~Test()...\n"; }
    };

    Test    test; // when is this initialized?
}

// testso.cpp
#include <dlfcn.h>
#include <iostream>

int main( int ac, char* av[] )
{
    if ( ac < 2 )
    {
        std::cerr << "Usage: " << av[0] << "library-name\n";
        return 1;
    }
    std::cerr << "Before dlopen()...\n";
    ::dlerror();
    void*    _handle(::dlopen( av[1], RTLD_NOW ));
    std::cerr << "After dlopen()...\n";
    if ( !_handle )
    {
        std::cerr << "Error: " << ::dlerror() << ", exiting...\n";
        return 2;
    }
    ::dlclose( _handle );
    std::cerr << "After dlclose()...\n";
    return 0;
}

Compiled and run (note Test() invocation before dlopen() returns):

$ g++ -o libtest.so -shared -fPIC libtest.cpp
$ g++ -o testso -ldl testso.cpp
$ ./testso ./libtest.so
Before dlopen()...
In Test()...
After dlopen()...
In ~Test()...
After dlclose()...
$ 

.

Upvotes: 5

Views: 3761

Answers (1)

First things first: dlopen() is incredibly platform specific and as with the case of early sickness symptoms and WebMD you should always consult your platform's relevant man page.

This holds true even though the dlopen() family of functions appears to conform to the IEEE Standard 1003.1, 2004 Edition, though I couldn't tell you how compliant today's systems are with such standards (e.g. Windows has a long standing history of shoddy POSIX compliance).


On OS/X / BSD, yes:

dlopen() examines the mach-o file specified by path. If the file is compatible with the current process and has not already been loaded into the current process, it is loaded and linked. After being linked, if it contains any initializer functions, they are called, before dlopen() returns.

Emphasis mine.


On Linux, yes:

Shared objects may export functions using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. Constructor functions are executed before dlopen() returns, and destructor functions are executed before dlclose() returns.

Emphasis mine.


On Solaris, yes:

As part of loading a new object, initialization code within the object is called before the dlopen() returns. This initialization is user code, and as such, can produce errors that can not be caught by dlopen().

Emphasis mine.

Upvotes: 5

Related Questions