2016-07-21 8 views
5

В основной программе я dlopen и dlclose (LoadLibrary и FreeLibrary соответственно) совместно используемой библиотекой. Общая библиотека содержит статическую переменную, которая создается на dlopen и уничтожается на dlclose. Такое поведение согласуется с MSVC 2008 и 2013, GCC 3.4.6 и Sunstudio 12.1. Однако с GCC 4.9.1 и GCC 5.2.1 деструктор больше не вызывался на dlclose. Вместо этого он вызывается перед выходом программы.Деструктор глобальной статической переменной в общей библиотеке не вызывается в dlclose

Особенность класса статических переменного, то, что в конструкторе, есть вызов шаблонных функций получить (в глобальном масштабе), которая возвращает локальную статический переменную.

Я был в состоянии воспроизвести такое поведение со следующим файлом один CPP скомпоновать в общей библиотеку:

#include <iostream> 

template <typename T> // In my actual code, i is of type T, however, this has no effect 
int get() 
{ 
    static int i = 0; 
    return i; 
} 

class Dictionary { 
public: 
    Dictionary() 
    { 
     std::cout << "Calling Constructor" << std::endl; 
     get<int>(); 
    } 
    ~Dictionary(){ 
     std::cout << "Calling Destructor" << std::endl; 
    } 

private: 
    Dictionary(const Dictionary&); 
    Dictionary& operator=(const Dictionary&); 
}; 
static Dictionary d; 

Я исследовал ухищрения, которые могут быть сделаны для того, чтобы иметь деструктор призвал dlclose, и пришел к выводу, следующее:

  • Если функция получить не шаблонный
  • иначе, если переменная я в функции получить не был статичным
  • иначе, если функция получить выполнена статической

кода основной программы заключается в следующем:

#include <dlfcn.h> 
#include <cassert> 
#include <string> 
#include <iostream> 

void* LoadLib(std::string name) 
{ 
     void* libInstance; 
     name = "lib" + name + ".so"; 
     libInstance = dlopen(name.c_str(), RTLD_NOW); 
     if (! libInstance) std::cout << "Loading of dictionary library failed. Reason: " << dlerror() << std::endl; 
     return libInstance; 
} 

bool UnloadLib(void* libInstance) 
{ 
    int ret = dlclose(libInstance); 
    if (ret == -1) 
    { 
     std::cout << "Unloading of dictionary library failed. Reason: " << dlerror() << std::endl; 
     return false; 
    } 
    return true; 
} 

int main() 
{ 
    void* instance = LoadLib("dll"); 
    assert(instance != 0); 

    assert(UnloadLib(instance)); 
    std::cout << "DLL unloaded" << std::endl; 
} 

Я построил двоичные файлы с помощью следующих команд:

g++ -m64 -g -std=c++11 -shared -fPIC dll.cpp -o libdll.so 
g++ -m64 -g -std=c++11 -ldl main.cpp -o main.out 

Выход, который я получаю, когда деструктор вызывается перед выходом программы, является fo llowing:

Calling Constructor 
DLL unloaded 
Calling Destructor 

выход я получаю, когда деструктор вызывается dlclose следующая:

Calling Constructor 
Calling Destructor 
DLL unloaded 

Вопросы:

  • Если изменение поведения между версиями GCC является не ошибка, не могли бы вы объяснить, почему деструктор не вызвал dlclose?
  • Можете ли вы объяснить, почему каждый двоеточие: почему деструктор называется dlclose в этом случае?
+0

звучит как сообщение об ошибке, вы должны сделать против GCC – GreatAndPowerfulOz

+1

статических переменных теперь потокобезопасная (они должны быть из-за Стандартные изменения C++, введенные в C++ 11). Возможно, это имеет значение. В Visual Studio 2013 этого не было реализовано, в то время как 2015 год. Может быть, вам следует протестировать с 2015 года. Только gcc 4.x и выше реализовали это, поэтому вы должны увидеть, играет ли статическая переменная потоковое требование. – PaulMcKenzie

ответ

3

Нет гарантии, что выгрузка (деструкторы вызывается) происходит на dlclose. В musl (в отличие от glibc) конструкторы запускают только при первом запуске библиотеки, а деструкторы запускаются только при выходе. Для переносного кода dlclose не может быть принято для немедленного выгрузки символов.

Поведение при разгрузке зависит от привязки символа glibc при динамической компоновке и не зависит от GCC.

Статическая переменная get :: i имеет привязку STB_GNU_UNIQUE.Для статических переменных во встроенных функциях уникальность объекта гарантируется компоновщиком ELF. Однако для динамической загрузки динамический компоновщик обеспечивает уникальность, маркируя символ STB_GNU_UNIQUE. Следовательно, другая попытка развернуть одну и ту же разделяемую библиотеку другим кодом будет искать символ и обнаруживать, что он уникален и возвращает существующий из таблицы уникальных символов. Символ с уникальной привязкой не может быть выгружен.

Уникальная привязка может быть отключена с помощью «-fno-gnu-unique», если не требуется.

Ссылка

Bug that I raised to GCC

STB_GNU_UNIQUE