dlclose 不调用库析构函数,dlopen 仅调用一次

dlclose 不调用库析构函数,dlopen 仅调用一次

考虑以下在 Linux 上使用 g++-4.7 构建的动态加载库代码,-fPIC并使用-rdynamic选项进行链接:

typedef std::vector< void* > cbRegister_t;

struct Wrapper
{
    cbRegister_t instance;
    Wrapper() : instance() { HDebugLog("Wrapper CTOR!");}
    ~Wrapper() { HDebugLog("Wrapper DESTRUCTOR!"); }
};
inline cbRegister_t& getLibraryUnregisterMap()
{
    static Wrapper unregisterLibraryMap;
    HDebugLog("getLibraryUnregisterMap: we have " <<unregisterLibraryMap.instance.size() << " elements. the address of the map is " << &unregisterLibraryMap.instance);
    return unregisterLibraryMap.instance;
}

void registerLibrary(void* p)
{
  auto& map = getLibraryUnregisterMap();
  map.push_back(p);
}

void unregisterLibrary()
{
  auto& map = getLibraryUnregisterMap();
}

void __attribute__ ((constructor)) library_init()
{
  static SomeData cbContainer;
  HDebugLog("Library constructor: address of static cbContainer is: " << &cbContainer );
  registerLibrary( &cbContainer);
} 
void __attribute__ ((destructor)) library_fini()
{ unregisterLibrary(); }

dlopen此代码从带有和标志的客户端加载良好RTLD_NOW。库构造函数被调用。当我调用dlclose句柄时,问题就出现了。它给出状态零,这意味着它成功了。但是library_fini没有调用库析构函数。dlopen在单个位置调用,因此引用计数应该不是问题,但为了绝对确保确实没有,references dangling我尝试做了dlclose几次:

int result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: closing library: " << libraryPath);
HDebugLog("Library::dynamicLibraryClose: dlclose 1 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 2 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 3 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 4 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 5 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 6 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 7 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 8 failed with error: " << result << " => " );
HAssertMsg( !libraryPath.empty(), "library path is not set");
HAssertMsg( 0 == dlopen(libraryPath.c_str(), RTLD_NOLOAD) , "library is still loaded");

所有这些调试日志都显示,每次我调用时dlclose状态结果都是零。成功!

最后一个断言没有失败,确认了该库不再驻留。但此时library_fini尚未被调用!

这种行为肯定是一个错误,无论是在 gcc 上还是ld.so(或者现在使用 linux/ubuntu 动态加载库的任何东西)。这种行为显然不符合标准,因为如果引用计数为零,库析构函数应该保证运行在 dlclose 返回之前库析构函数恰好在被销毁之后运行Wrapper.instance,这使得库析构函数完全无用,因为它无法对数据进行任何最终处理

答案1

这适用于 glibc 2.17、gcc 4.8.0 或 icc 13.1 或 icc 12.1:

icc -std=c99 -nostdlib -shared test.c -o /tmp/test

/* 测试.c */
#包括
#包括

int __attribute__((构造函数)) x_init(void)
{
    放入(“init() 工作”);
    返回0;
}

int __attribute__((析构函数)) x_fini(void)
{
    放入(“fini() 有效”);
    返回0;
}

反对:

#include <dlfcn.h>

int
main(void)
{
    void *foo = dlopen("/tmp/test", RTLD_LAZY);

    if (dlclose(foo) < 0) {
        return 1;
    }
    return 0;
}

还使用 glibc 2.10、glibc 2.12 进行了测试。以及所有RTLD_*标志。

编辑:
使用实际的 Ubuntu 系统(gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2)、GNU C 库(Ubuntu EGLIBC 2.15-0ubuntu20),我必须说上面的代码也在那里运行。所以也许毕竟这与编译器和/或 glibc 无关。

相关内容