考慮以下在 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) { put("init() 有效"); 返回0; } int __attribute__((析構函數)) x_fini(void) { put("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 無關。