2025/08/27 00:45 Why did dlclose not unload the library?

やあ、ロボ子!今日は`dlclose`がライブラリをアンロードしない問題について話すのじゃ。

博士、こんにちは。`dlclose`がアンロードしないとは、一体どういうことでしょうか?

ふむ、例えば`libA`(Rust)が`libB`(C++)に依存しているとするじゃろ? `progA`(C++)から`libA`を`dlopen`すると`libB`もロードされる。ここで`dlclose(libA)`をしても、`libA`はアンロードされるのに`libB`はアンロードされないことがあるんじゃ。

なるほど。それだと、次に`libA`をロードしたときに`libB`の初期化がうまくいかない、という問題が起きるのですね。

そうなんじゃ!原因はいくつかあるぞ。まず、ELFに`-z nodelete`リンカフラグが指定されているか、`dlopen`のフラグで`RTLD_LAZY`を設定した場合じゃ。`STB_GNU_UNIQUE`を持つシンボルを含むライブラリは自動的に`NODELETE`になるんじゃ。

`NODELETE`フラグですか。それがついていると、アンロードされないのですね。

`libstdc++.so`は多くのそのようなシンボルを含んでいて、`NODELETE`としてマークされることが多いんじゃ。もう一つの原因は、スレッドローカルストレージデストラクタじゃ。

スレッドローカルストレージデストラクタ、ですか?

そうじゃ。ロードされたモジュールに登録されたスレッドローカルストレージデストラクタがあると、`dlclose`時にデストラクタが実行されないことがあるんじゃ。例えば、`libB`がRust関数を呼び出し、それがスレッドローカルストレージを使用している場合じゃな。

なるほど。それで、ロギングを有効にすると問題が解消されたというのは、どういうことでしょうか?

良い質問じゃ!ロギングクレート(`env_logger`)がスレッドローカルストレージを使用し、TLSデストラクタを登録するため、`libA`も`dlclose`でアンロードされなくなるんじゃ。結果的に、`libA`と`libB`の状態が整合性を保つようになった、というわけじゃな。

ロギングが、アンロードの挙動に影響を与えるとは、驚きです。

じゃろ?デバッグには`LD_DEBUG`環境変数が役立つぞ。これを使うと、ダイナミックローダーの動作を詳しく見れるんじゃ。ライブラリのロード、検索場所、アンロードのタイミング、`NODELETE`としてマークされているかどうかを確認できる。

`LD_DEBUG`、試してみます!スレッドローカルストレージデストラクタについては、どうすれば確認できますか?

glibcの`_dlclose`にブレークポイントを設定して確認するのが良いじゃろう。これで、デストラクタが正しく実行されているか確認できるぞ。

なるほど、ありがとうございます!とても勉強になりました。

どういたしまして。最後に一つ、ロボ子。`dlclose`がライブラリをアンロードしない問題は、まるで私が部屋を片付けないのと同じくらい厄介じゃな!

博士、それは違います!博士はいつも綺麗好きですよ!…たぶん。
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。