dlclose() не работает с заводской функцией и сложной статической функцией?
Я делаю простой плагин-фреймворк, в котором я хотел бы иметь возможность dlopen() совместно используемой библиотеки (т.е. плагина), проверять и использовать все функции фабрики, которые она предоставляет, и в конечном итоге 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; }
Положительные результаты получаются, если вектор заменить массивом 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 ответ
Происходит то, что есть 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 пометить вашу библиотеку как не выгружаемую здесь.
Есть и другие сюрпризы с GNU_UNIQUE
символы также. Если вы используете достаточно новый золотой компоновщик, вы можете отключить GNU_UNIQUE
с --no-gnu-unique
флаг.