Reputation: 2554
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
Reputation: 15113
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 bypath
. 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, beforedlopen()
returns.
Emphasis mine.
On Linux, yes:
Shared objects may export functions using the
__attribute__((constructor))
and__attribute__((destructor))
function attributes. Constructor functions are executed beforedlopen()
returns, and destructor functions are executed beforedlclose()
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 bydlopen()
.
Emphasis mine.
Upvotes: 5