dlclose はライブラリのデストラクタを呼び出さず、dlopen は一度だけ呼び出されます。

dlclose はライブラリのデストラクタを呼び出さず、dlopen は一度だけ呼び出されます。

-fPICLinux 上の g++-4.7 でビルドされ、オプションでリンクされた動的ロード ライブラリの次のコードを考えてみます-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(); }

このコードは、フラグを使用してクライアントから正常にロードされますdlopenRTLD_NOWライブラリ コンストラクターが呼び出されます。問題は、dlcloseハンドルを呼び出すときに発生します。ステータス 0 が返され、成功したことを意味します。ただし、ライブラリ デストラクタlibrary_finiは呼び出されません。dlopenは 1 か所で呼び出されるため、参照カウントは問題にならないはずですが、実際に問題がないことを確認するために、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/Ubuntu を使用してライブラリを動的にロードするその他のもの) のどちらかで、間違いなくバグです。この動作は、標準で規定されている動作ではありません。ライブラリのデストラクタは、参照カウントがゼロの場合に実行されることが保証されている必要があるためです。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 の問題ではないのかもしれません。

関連情報