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/человечность для динамичной загрузки библиотек в эти дни). Это поведение ясно не то, что говорит стандарт, потому что деструкторы библиотеки, как должны гарантировать, будут выполнены, если подсчет ссылок будет нулем прежде dlclose возвраты. за деструкторами библиотеки, оказывается, бегут Wrapper.instance уничтожается, который представляет абсолютно бесполезный деструктор библиотеки, как не может сделать любое завершение данных

1
задан 3 April 2013 в 06:39

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 */
#include 
#include 

int __attribute__((constructor)) x_init(void)
{
    puts("init() works");
    return 0;
}

int __attribute__((destructor)) x_fini(void)
{
    puts("fini() works");
    return 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.

0
ответ дан 3 April 2013 в 06:39

Другие вопросы по тегам:

Похожие вопросы: