considere el siguiente código para una biblioteca cargada dinámicamente construida con g++-4.7 en Linux -fPIC
y vinculada con -rdynamic
la opción:
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 se carga bien desde un cliente con dlopen
y la RTLD_NOW
bandera. Se llama al constructor de la biblioteca. El problema ocurre cuando llamo dlclose
al identificador. Da estado cero, lo que significa que fue exitoso. Pero library_fini
no se llama al destructor de la biblioteca. dlopen
se llama en un solo lugar, por lo que el recuento de referencias no debería ser un problema, pero para estar absolutamente seguro de que realmente no hay ninguna, references dangling
intenté hacer esto dlclose
varias veces:
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 esos registros de depuración muestran que cada vez que llamo, dlclose
el resultado del estado es cero. ¡Éxito!
La última afirmación, que no falla, confirma que la biblioteca ya no es residente. ¡Pero a estas alturas library_fini
aún no ha sido llamado!
Este comportamiento es definitivamente un error, ya sea en gcc o ld.so
(o lo que sea que esté usando Linux/ubuntu para cargar bibliotecas dinámicamente en estos días). Este comportamiento claramente no es lo que dice el estándar, porque se debe garantizar que los destructores de biblioteca se ejecuten si el recuento de referencias es cero.antes de dlclose regresa. Los destructores de la biblioteca se ejecutan después de Wrapper.instance
ser destruido, lo que hace que el destructor de la biblioteca sea completamente inútil, ya que no puede finalizar los datos.
Respuesta1
Esto funciona con glibc 2.17, gcc 4.8.0 o icc 13.1 o icc 12.1:
icc -std=c99 -nostdlib -shared test.c -o /tmp/test
/* prueba.c */ #incluir #incluir int __atributo__((constructor)) x_init(vacío) { pone("init() funciona"); devolver 0; } int __atributo__((destructor)) x_fini(vacío) { puts("fini() funciona"); devolver 0; }
contra:
#include <dlfcn.h>
int
main(void)
{
void *foo = dlopen("/tmp/test", RTLD_LAZY);
if (dlclose(foo) < 0) {
return 1;
}
return 0;
}
También probado con glibc 2.10, glibc 2.12. Y todas RTLD_*
las banderas.
Editar:
Usando un sistema Ubuntu real (gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2), Biblioteca GNU C (Ubuntu EGLIBC 2.15-0ubuntu20), debo decir que el código anterior también funciona allí. Entonces, tal vez, después de todo, no se trate del compilador y/o glibc.