Dlclose () not working with factory function and complex static function?

I am creating a simple plugin structure in which I would like to be able to use dlopen () a shared library (like a plugin), test and use any factory functions and ultimately dlclose (), leaving no traces.

My factory system is trivial, with one exported function that returns a pointer to a common base class. To check if the plugin is loaded correctly, I have a static object whose destructor installs bool from the main program.

Here's the main program:

// dltest.cpp follows. Compile with g++ -std=c++0x dltest.cpp -o dltest -ldl #include <dlfcn.h> #include <iostream> using namespace std; int main(int argc, char** argv) { if (argc > 1) { void* h = dlopen(argv[1], RTLD_NOW|RTLD_LOCAL); if (!h) { cerr << "ERROR: " << dlerror() << endl; return 1; } bool isFinilized = false; *(bool**)dlsym(h, "g_finilized") = &isFinilized; cout << boolalpha << isFinilized << endl; if (dlclose(h)) { cerr << "ERROR: " << dlerror() << endl; return 2; } cout << boolalpha << isFinilized << endl; } return 0; } 

And the plugin code:

 // libempty.cpp follows. Compile with g++ -std=c++0x libempty.cpp -o libempty.so -fPIC -shared #include <iostream> #include <vector> using namespace std; bool* g_finilized = nullptr; struct Finilizer { ~Finilizer() { cout << "~Finilizer()" << endl; if (g_finilized) *g_finilized = true; } } g_finilizer; class Base { public: virtual void init() = 0; }; class Foo: public Base { virtual void init() { static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; } }; extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

If executed, exit:

 false false ~Finilizer() 

This shows that the dlclose () call is not working properly, and the library was not unloaded before the program exited.

However, if we move the vector outside of the function, so the last 8 lines are read:

 class Foo: public Base { virtual void init() { } }; static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

Then dlclose () works correctly, and the output is:

 false ~Finilizer() true 

The same results are generated if the vector remains in the function but the factory is not exported:

 class Foo: public Base { virtual void init() { static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; } }; //extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

Positive results are found if the vector is replaced by an array of C:

 class Foo: public Base { virtual void init() { static const float ns[] = { 0.f, 0.75f, 0.67f, 0.87f }; } }; extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

Is this a bug in GCC / Linux? Is there any workaround so that complex objects can be statically declared in a factorized class member function?

0
source share
1 answer

What happens is the STB_GNU_UNIQUE symbol in libempty.so :

 readelf -Ws libempty.so | grep _ZGVZN3Foo4initEvE2ns 91: 0000000000203e80 8 OBJECT UNIQUE DEFAULT 25 _ZGVZN3Foo4initEvE2ns 77: 0000000000203e80 8 OBJECT UNIQUE DEFAULT 25 _ZGVZN3Foo4initEvE2ns 

The problem is that the STB_GNU_UNIQUE characters are quite unintuitive and persist when dlopen / dlclose .

Using this symbol causes glibc to mark your library as non-reloadable here .

There are other surprises with GNU_UNIQUE characters. If you use a fairly recent gold linker, you can disable GNU_UNIQUE with the --no-gnu-unique flag.

+3
source

All Articles