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)
{
    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 無關。

相關內容