23

插件1.cpp:

#include <iostream>

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

主机.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;
}

构建并运行:

>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

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

4

1 回答 1

16

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

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

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


从您的问题中不清楚您使用的是什么版本的 gcc、链接器和标准 C 库。另外,您提供的代码不符合POSIX标准,因为没有定义RTDL_NOWRTDL_LOCAL定义宏。它们是RTLD_NOWRTLD_LOCAL(参见dlopen)。

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

-fuse-cxa-atexit

使用 __cxa_ atexit 函数而不是 atexit 函数为具有静态存储持续时间的对象注册析构函数。此选项对于完全符合标准的静态析构函数处理是必需的,但仅当您的 C 库支持 __cxa_atexit 时才有效。

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

于 2010-09-28T16:38:13.617 回答