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
を呼び出すと発生します。ステータスがゼロになり、成功したことを意味します。ただし、ライブラリデストラクタ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/ubuntuを使用してライブラリを動的にロードするもの)のバグです。参照カウントがゼロの場合、ライブラリのデストラクタが実行されることが保証されるため、この動作は明らかに標準では示されていませんdlcloseが戻る前。ライブラリデストラクタはWrapper.instance
が破壊された後に実行されるため、データのファイナライズができないため、ライブラリデストラクタは完全に役に立たなくなります。
これは、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についてのものではないのかもしれません。