dlclose는 라이브러리 소멸자를 호출하지 않으며, dlopen은 한 번만 호출됩니다.

dlclose는 라이브러리 소멸자를 호출하지 않으며, dlopen은 한 번만 호출됩니다.

Linux에서 g++-4.7로 빌드되고 옵션 -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핸들을 호출할 때 문제가 발생합니다 . 상태 0을 제공하며 이는 성공했음을 의미합니다. 그러나 라이브러리 소멸자 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상태 결과가 0임을 보여줍니다. 성공!

실패하지 않는 마지막 어설션은 라이브러리가 더 이상 상주하지 않음을 확인합니다. 하지만 이 시점에서는 library_fini아직 호출되지 않았습니다!

이 동작은 gcc나 ld.so(또는 ​​요즘 동적으로 라이브러리를 로드하기 위해 linux/ubuntu를 사용하는 모든 것)에서 확실히 버그입니다. 참조 카운트가 0인 경우 라이브러리 소멸자가 실행되도록 보장되어야 하기 때문에 이 동작은 분명히 표준에서 말하는 것과 다릅니다.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

/* 테스트.c */
#포함하다
#포함하다

int __attribute__((생성자)) x_init(void)
{
    puts("init()가 작동합니다.");
    0을 반환합니다.
}

int __attribute__((소멸자)) x_fini(void)
{
    puts("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에 관한 것이 아닐 수도 있습니다.

관련 정보