dlclose ruft keine Bibliotheksdestruktoren auf, dlopen wird nur einmal aufgerufen

dlclose ruft keine Bibliotheksdestruktoren auf, dlopen wird nur einmal aufgerufen

Betrachten Sie den folgenden Code für eine dynamisch geladene Bibliothek, die mit G++-4.7 unter Linux erstellt -fPICund mit -rdynamicder Option verknüpft wurde:

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(); }

Dieser Code wird von einem Client mit dlopenund dem RTLD_NOWFlag problemlos geladen. Der Bibliothekskonstruktor wird aufgerufen. Das Problem tritt auf, wenn ich dlcloseden Handle aufrufe. Es gibt den Status Null zurück, was bedeutet, dass es erfolgreich war. Aber der Bibliotheksdestruktor library_finiwird nicht aufgerufen. dlopenwird an einer einzigen Stelle aufgerufen, also sollte die Referenzzählung kein Problem sein, aber um absolut sicher zu sein, dass es wirklich keine gibt, habe references danglingich versucht, dies dlclosemehrmals zu tun:

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");

dlcloseAlle diese Debug-Protokolle zeigen, dass das Statusergebnis bei jedem Aufruf Null ist. Erfolg!

Die letzte Assertion, die nicht fehlschlägt, bestätigt, dass die Bibliothek nicht mehr resident ist. Sie library_finiwurde zu diesem Zeitpunkt aber noch nicht aufgerufen!

Dieses Verhalten ist definitiv ein Fehler, entweder bei gcc oder ld.so(oder was auch immer Linux/Ubuntu heutzutage verwendet, um Bibliotheken dynamisch zu laden). Dieses Verhalten entspricht eindeutig nicht dem, was der Standard vorsieht, da Bibliotheksdestruktoren garantiert ausgeführt werden sollten, wenn der Referenzzähler Null ist.bevor dlclose zurückkehrt. Bibliotheksdestruktoren werden ausgeführt, nachdem die Wrapper.instancezerstört wurde, was den Bibliotheksdestruktor völlig nutzlos macht, da er keine Finalisierung der Daten durchführen kann

Antwort1

Dies funktioniert mit glibc 2.17, gcc 4.8.0 oder icc 13.1 oder icc 12.1:

icc -std=c99 -nostdlib -shared test.c -o /tmp/test

/* test.c */
#enthalten
#enthalten

int __attribute__((Konstruktor)) x_init(void)
{
    setzt("init() funktioniert");
    gebe 0 zurück;
}

int __attribute__((Destruktor)) x_fini(void)
{
    setzt("fini() funktioniert");
    gebe 0 zurück;
}

gegen:

#include <dlfcn.h>

int
main(void)
{
    void *foo = dlopen("/tmp/test", RTLD_LAZY);

    if (dlclose(foo) < 0) {
        return 1;
    }
    return 0;
}

Auch getestet mit glibc 2.10, glibc 2.12. Und allen RTLD_*Flags.

Bearbeiten:
Ich muss sagen, dass der obige Code auch auf einem aktuellen Ubuntu-System (gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2) und der GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu20) funktioniert. Vielleicht liegt es also doch nicht am Compiler und/oder an glibc.

verwandte Informationen