dlclose()不适用于工厂功能&复杂的静态函数?

时间:2012-06-15 12:31:48

标签: c++ linux gcc c++11 dlopen

我正在创建一个简单的插件框架,我希望能够dlopen()一个共享库(即插件),检查并使用提供的任何工厂函数并最终dlclose()它,不留任何痕迹

我的工厂系统很简单,只有一个导出函数,它返回一个指向公共Base类的指针。为了检查插件是否已正确卸载,我有一个静态对象,析构函数从主程序中设置一个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; }

如果用C数组代替载体,则会发现阳性结果:

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中的错误吗?是否有任何解决方法,以便复杂对象可以在分解类的成员函数中静态声明?

1 个答案:

答案 0 :(得分:4)

正在发生的事情是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 surprises也有GNU_UNIQUE个符号。如果您使用足够近期的黄金链接器,则可以使用GNU_UNIQUE标志禁用--no-gnu-unique