-fPIC
Linux 上の 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(); }
このコードは、フラグを使用してクライアントから正常にロードされますdlopen
。RTLD_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 の問題ではないのかもしれません。