2012-06-15 3 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 Результаты, полученные в результате, можно найти, если вектор заменен на массив 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? Существует ли какое-либо обходное решение, чтобы сложные объекты могли статически объявляться в членной функции факторизованного класса?

+0

Откуда у вас возникла мысль, что одно поведение «правильно», а другое - нет? Я не думаю, что POSIX.1-2001 делает какие-либо гарантии так или иначе. –

+0

Код ассемблера, сгенерированный в обоих случаях, значительно отличается. Когда 'ns' определяется как статическая локальная переменная, многие дополнительные обработчики регистрируются с помощью' __cxa_atexit() ', а поведение trader' ld-linux.so' показывает, что дополнительные символы разрешаются. К сожалению, Intel C++ не поддерживает списки инициализаторов, и трудно проверить, относится ли это поведение только к GCC. –

+0

@NikolaiNFetissov: Согласно странице man: «Функция 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 surprises с GNU_UNIQUE символов. Если вы используете достаточно недавний золотой линкер, вы можете отключить GNU_UNIQUE с флагом --no-gnu-unique.

Смежные вопросы