dlclose()不会调用全局对象的析构函数

And*_*ack 22 c++ dll gcc atexit dlopen

plugin1.cpp:

#include <iostream>

static class TestStatic {
public:
  TestStatic() {
     std::cout << "TestStatic create" << std::endl;
  }
  ~TestStatic() {
     std::cout << "TestStatic destroy" << std::endl;
  }
} test_static;
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

构建和运行:

>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
Run Code Online (Sandbox Code Playgroud)

为什么TestStatic :: ~TestStatic在'exit()'处调用但不在'dlclose()'处调用?

小智 16

当程序以相反的构造顺序退出时,C++标准要求为全局对象调用析构函数.大多数实现都通过调用C库atexit例程来注册析构函数来处理这个问题.这是有问题的,因为1999 C标准只要求实现支持32个注册函数,尽管大多数实现支持更多.更重要的是,它完全没有处理大多数实现中通过在程序终止之前调用dlclose从正在运行的程序映像中删除DSO的能力.

在GCC的更高版本中解决了该问题,包括C/C++标准库和链接器.基本上,C++析构函数应该使用__cxa_atexit函数而不是atexit(3)进行注册.

有关完整的技术细节__cxa_atexit,请参阅Itanium C++ ABI规范.


从您的问题中不清楚您正在使用的gcc,链接器和标准C库的版本.此外,您提供的代码不符合POSIX标准,因为没有定义RTDL_NOWRTDL_LOCAL宏.他们是RTLD_NOWRTLD_LOCAL(见dlopen).

如果您的C标准库不支持__cxa_atexit,您可能需要通过指定-fno-use-cxa-atexitgcc标志来禁用它:

- 保险丝-CXA-的atexit

使用__cxa_ atexit函数而不是atexit函数为具有静态存储持续时间的对象注册析构函数.完全符合标准的静态析构函数处理需要此选项,但只有在C库支持__cxa_atexit时才能使用此选项.

但是,这可能会导致以不同的顺序调用析构函数或根本不调用析构函数的问题.因此,在__cxa_atexit支持破坏或根本不支持的情况下,最好的解决方案是不要在共享库中使用具有析构函数的静态对象.