2012-06-15 29 views
0

我做一個簡單的插件框架,我想能夠dlopen()的共享庫(即插件),檢查和使用任何工廠的功能是提供並最終dlclose()它,不留痕跡。dlclose()不帶工廠功能及在功能複雜的靜態工作?

我的工廠制度是微不足道的,只有一個導出的函數返回一個指向一個共同的基類。爲了檢查插件是否已正確卸載,我有一個靜態對象,它的析構函數從主程序中設置了一個bool。

這裏的主要程序:

// dltest.cpp follows. Compile with g++ -std=c++0x dltest.cpp -o dltest -ldl 
#include <dlfcn.h> 
#include <iostream> 
using namespace std; 
int main(int argc, char** argv) 
{ 
    if (argc > 1) 
    { 
     void* h = dlopen(argv[1], RTLD_NOW|RTLD_LOCAL); 
     if (!h) 
     { 
      cerr << "ERROR: " << dlerror() << endl; 
      return 1; 
     } 
     bool isFinilized = false; 
     *(bool**)dlsym(h, "g_finilized") = &isFinilized; 
     cout << boolalpha << isFinilized << endl; 
     if (dlclose(h)) 
     { 
      cerr << "ERROR: " << dlerror() << endl; 
      return 2; 
     } 
     cout << boolalpha << isFinilized << endl; 
    } 
    return 0; 
} 

和插件的代碼是:

// libempty.cpp follows. Compile with g++ -std=c++0x libempty.cpp -o libempty.so -fPIC -shared 
#include <iostream> 
#include <vector> 
using namespace std; 
bool* g_finilized = nullptr; 
struct Finilizer 
{ 
    ~Finilizer() 
    { 
     cout << "~Finilizer()" << endl; 
     if (g_finilized) *g_finilized = true; 
    } 
} g_finilizer; 
class Base 
{ 
public: 
    virtual void init() = 0; 
}; 
class Foo: public Base 
{ 
    virtual void init() 
    { 
     static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; 
    } 
}; 
extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

如果執行,將輸出結果是:

false 
false 
~Finilizer() 

這表明調用dlclose( )不能按預期工作,並且在程序退出之前不會卸載該庫。

然而,如果我們移動載體的功能之外,所以最後8行閱讀:

class Foo: public Base 
{ 
    virtual void init() 
    { 
    } 
}; 
static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; 
extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

然後dlclose()正常工作,輸出:

false 
~Finilizer() 
true 

同如果向量留在函數中但未導出工廠,則會生成結果:

class Foo: public Base 
{ 
    virtual void init() 
    { 
     static const vector<float> ns = { 0.f, 0.75f, 0.67f, 0.87f }; 
    } 
}; 
//extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

Pos

class Foo: public Base 
{ 
    virtual void init() 
    { 
     static const float ns[] = { 0.f, 0.75f, 0.67f, 0.87f }; 
    } 
}; 
extern "C" __attribute__ ((visibility ("default"))) Base* newBase() { return new Foo; } 

這是在GCC/Linux的一個錯誤:如果矢量取代有C數組itive結果發現?是否有任何解決方法,以便可以在分解類的成員函數中靜態聲明覆雜對象?

+0

你從哪裏得到這樣的想法:一種行爲是「正確的」,另一種行爲是不正確的?我不認爲POSIX.1-2001會以某種方式做出任何保證。 –

+0

在這兩種情況下生成的彙編代碼差別很大。當'ns'被定義爲一個靜態局部變量時,會有很多額外的處理程序向'__cxa_atexit()'註冊,跟蹤'ld-linux.so'行爲表明正在解析附加符號。不幸的是,英特爾C++不支持初始化列表,並且很難測試這種行爲是否僅限於GCC。 –

+0

@NikolaiNFetissov:根據手冊頁:「函數dlclose()遞減動態庫句柄句柄的引用計數,如果引用計數下降到零,並且沒有其他加載的庫使用它中的符號,則卸載動態庫「。我沒有看到明顯的原因,爲什麼它應該在某些測試案例中卸載,而不是在其他案例中卸載。 – gavwould

回答

3

發生了什麼事是,有一個STB_GNU_UNIQUE符號libempty.so

readelf -Ws libempty.so | grep _ZGVZN3Foo4initEvE2ns 
91: 0000000000203e80  8 OBJECT UNIQUE DEFAULT 25 _ZGVZN3Foo4initEvE2ns 
77: 0000000000203e80  8 OBJECT UNIQUE DEFAULT 25 _ZGVZN3Foo4initEvE2ns 

的問題是,STB_GNU_UNIQUE符號工作完全是非直觀的,和整個dlopen/dlclose通話持續。

使用該符號力量的glibc來標記庫作爲非卸here

other surprisesGNU_UNIQUE符號爲好。如果使用足夠新的黃金鍊接,你可以用--no-gnu-unique標誌禁用GNU_UNIQUE