考虑以下在 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 无关。