dlclose() does not call the destructor of global o

2019-03-12 13:32发布

问题:

plugin1.cpp:

#include <iostream>

static class TestStatic {
public:
  TestStatic() {
     std::cout << "TestStatic create" << std::endl;
  }
  ~TestStatic() {
     std::cout << "TestStatic destroy" << std::endl;
  }
} test_static;

host.cpp

#include <dlfcn.h>
#include <iostream>
int main(int argc,char *argv[]) {
   void* handle = dlopen("./plugin1.so",RTLD_NOW | RTLD_LOCAL );
   dlclose(handle);
   return 0;
}

build and run:

>g++ -c plugin1.cpp -o plugin1.o -fPIC
>g++ -shared plugin.o -o plugin1.so
>g++ host.cpp -o host -ldl
>./host
>TestStatic create
>Segmentation fault

why TestStatic::~TestStatic called at 'exit()' but not at 'dlclose()' ?

回答1:

The C++ Standard requires that destructors be called for global objects when a program exits in the opposite order of construction. Most implementations have handled this by calling the C library atexit routine to register the destructors. This is problematic because the 1999 C Standard only requires that the implementation support 32 registered functions, although most implementations support many more. More important, it does not deal at all with the ability in most implementations to remove DSOs from a running program image by calling dlclose prior to program termination.

This problem is addressed in later versions of GCC including C/C++ standard library and linker. Basically, C++ destructors should be registered using __cxa_atexit function instead of atexit (3).

For the full technical details on __cxa_atexit, see Itanium C++ ABI specification.


It is not clear from your question what version of the gcc, linker and standard C library you are using. Plus, the code you have provided does not meet POSIX standard as there are no RTDL_NOW or RTDL_LOCAL macros defined. They are RTLD_NOW and RTLD_LOCAL (see dlopen).

If your C standard library does not support __cxa_atexit, you may need to disable it by specifying -fno-use-cxa-atexit gcc flag:

-fuse-cxa-atexit

Register destructors for objects with static storage duration with the __cxa_ atexit function rather than the atexit function. This option is required for fully standards-compliant handling of static destructors, but will only work if your C library supports __cxa_atexit.

But that might result in a problem where destructors are called in different order or not called at all. So the best solution in case of broken __cxa_atexit support or no support at all is not to use static objects with destructors in your shared libraries.