considere o seguinte código para uma biblioteca carregada dinâmica construída com g++-4.7 no Linux -fPIC
e vinculada à -rdynamic
opção:
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(); }
Este código carrega bem de um cliente com dlopen
o RTLD_NOW
sinalizador. O construtor da biblioteca é chamado. O problema acontece quando eu ligo dlclose
para o identificador. Dá status zero, o que significa que foi bem-sucedido. Mas o destruidor da biblioteca library_fini
não é chamado. dlopen
é chamado em um único local, então a contagem de referências não deve ser um problema, mas para ter certeza absoluta de que realmente não há nenhuma, references dangling
tentei fazer dlclose
várias vezes:
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");
Todos esses logs de depuração mostram que cada vez que eu chamo, dlclose
o resultado do status é zero. Sucesso!
A última afirmação, que não falha, confirma que a biblioteca não é mais residente. Mas neste momento library_fini
ainda não foi chamado!
Esse comportamento é definitivamente um bug, seja no gcc ou ld.so
(ou o que quer que esteja usando o linux/ubuntu para carregar bibliotecas dinamicamente atualmente). Esse comportamento claramente não é o que o padrão diz, porque os destruidores de bibliotecas devem ter execução garantida se a contagem de referência for zeroantes de dlclose retornar. os destruidores da biblioteca são executados após a Wrapper.instance
destruição, o que torna o destruidor da biblioteca completamente inútil, pois não é capaz de fazer nenhuma finalização dos dados
Responder1
Isso funciona com glibc 2.17, gcc 4.8.0 ou icc 13.1 ou icc 12.1:
icc -std=c99 -nostdlib -shared test.c -o /tmp/test
/* teste.c */ #incluir #incluir int __attribute__((construtor)) x_init(void) { puts("init() funciona"); retornar 0; } int __attribute__((destruidor)) x_fini(void) { puts("fini() funciona"); retornar 0; }
contra:
#include <dlfcn.h>
int
main(void)
{
void *foo = dlopen("/tmp/test", RTLD_LAZY);
if (dlclose(foo) < 0) {
return 1;
}
return 0;
}
Também testado com glibc 2.10, glibc 2.12. E todas RTLD_*
as bandeiras.
Editar:
Usando um sistema Ubuntu real (gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2), Biblioteca GNU C (Ubuntu EGLIBC 2.15-0ubuntu20), devo dizer que o código acima também funciona lá. Então talvez afinal não se trate do compilador e/ou glibc.