dlclose не вызывает библиотечные деструкторы, dlopen вызывается только один раз
Рассмотрим следующий код для динамически загружаемой библиотеки, созданной с помощью g++-4.7 в Linux, -fPIC
и связано с -rdynamic
опция:
typedef std::vector< void* > cbRegister_t;
struct Wrapper
{
cbRegister_t instance;
Wrapper() : instance() { HDebugLog("Wrapper CTOR!");}
~Wrapper() { HDebugLog("Wrapper DESTRUCTOR!"); }
};
inline cbRegister_t& getLibraryUnregisterMap()
{
static Wrapper unregisterLibraryMap;
HDebugLog("getLibraryUnregisterMap: we have " <<unregisterLibraryMap.instance.size() << " elements. the address of the map is " << &unregisterLibraryMap.instance);
return unregisterLibraryMap.instance;
}
void registerLibrary(void* p)
{
auto& map = getLibraryUnregisterMap();
map.push_back(p);
}
void unregisterLibrary()
{
auto& map = getLibraryUnregisterMap();
}
void __attribute__ ((constructor)) library_init()
{
static SomeData cbContainer;
HDebugLog("Library constructor: address of static cbContainer is: " << &cbContainer );
registerLibrary( &cbContainer);
}
void __attribute__ ((destructor)) library_fini()
{ unregisterLibrary(); }
Этот код нормально загружается с клиента с dlopen
и RTLD_NOW
флаг. Конструктор библиотеки называется. Проблема возникает, когда я звоню dlclose
на ручке. Это дает статус ноль, что означает, что он был успешным. Но деструктор библиотеки library_fini
не называется. dlopen
вызывается в одном месте, поэтому подсчет ссылок не должен быть проблемой, но для того, чтобы быть абсолютно уверенным, что на самом деле нет references dangling
я пытался сделать dlclose
несколько раз:
int result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: closing library: " << libraryPath);
HDebugLog("Library::dynamicLibraryClose: dlclose 1 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 2 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 3 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 4 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 5 failed with error: " << result << " => " );
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 6 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 7 failed with error: " << result << " => ");
result = dlclose(handle);
HDebugLog("Library::dynamicLibraryClose: dlclose 8 failed with error: " << result << " => " );
HAssertMsg( !libraryPath.empty(), "library path is not set");
HAssertMsg( 0 == dlopen(libraryPath.c_str(), RTLD_NOLOAD) , "library is still loaded");
Все эти журналы отладки показывают, что каждый раз, когда я звоню dlclose
результат статуса равен нулю. Успех!
Последнее утверждение, которое не является ошибочным, подтверждает, что библиотека больше не является резидентной. Но в этот момент library_fini
еще не был вызван!
Такое поведение определенно является ошибкой, либо на GCC или ld.so
(или что бы то ни было использует linux/ubuntu для динамической загрузки библиотек в наши дни). Такое поведение явно не соответствует стандарту, потому что деструкторы библиотеки должны быть гарантированно запущены, если счетчик ссылок равен нулю до возврата dlclose. Деструкторы библиотеки случайно запускаются после Wrapper.instance
уничтожается, что делает деструктор библиотеки совершенно бесполезным, так как не может выполнить какую-либо финализацию данных
1 ответ
Это работает с glibc 2.17, gcc 4.8.0 или icc 13.1 или icc 12.1:
icc -std=c99 -nostdlib -shared test.c -o /tmp/test
/ * test.c * / #включают #включают int __attribute __ ((конструктор)) x_init(пустота) { ставит ("init() работает"); вернуть 0; } int __attribute __ ((деструктор)) x_fini(void) { ставит ("fini() работает"); вернуть 0; }
против:
#include <dlfcn.h>
int
main(void)
{
void *foo = dlopen("/tmp/test", RTLD_LAZY);
if (dlclose(foo) < 0) {
return 1;
}
return 0;
}
Также протестирован с glibc 2.10, glibc 2.12. И все RTLD_*
флаги.
Редактировать:
Используя настоящую систему Ubuntu (gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2), библиотеку GNU C (Ubuntu EGLIBC 2.15-0ubuntu20), я должен сказать, что приведенный выше код также работает там. Так что, возможно, все-таки речь идет не о компиляторе и / или glibc.