2006年4月23日

子プロセスに LD_PRELOAD を継承させない

Linux で LD_PRELOAD 環境変数を使うと共有オブジェクト (共有ライブラリ) のプリロードを行うことができます。通常、LD_PRELOAD が設定されている間は、あるプロセスから呼んだ子プロセスも同様にプリロードを行いますが、場合によっては子プロセスにはプリロードさせたくないときもあります。

 

たとえば、make に対して foo.so をプリロードさせるつもりで、 LD_PRELOAD=./foo.so make と実行すると、make が呼び出すあらゆるコマンドも foo.so をプリロードします。 その結果、gcc や gcc の呼び出すプログラム cc1, as, collect2, ld まで foo.so をプリロードします。

foo.so が make の挙動を変えることのみを目的としていた場合、 gcc まで影響を受けてしまうのは困ります。また、 make のすべての子プロセスが foo.so をプリロードするため、プリロードにかかるオーバーヘッドも気になります。

このような場合、 foo.so に次のような関数を入れておけば子プロセスに LD_PRELOAD が継承されることを防げます。GCC の拡張機能である __attribute__((constructor)) により、 unset_ld_preload() は main() に到達する前に呼ばれます。

void __attribute__((constructor)) unset_ld_preload() {
    unsetenv("LD_PRELOAD");
}

foo.so に上のような関数を追加することが難しい場合は、上の関数だけを含んだ共有オブジェクト unset_ld_preload.so を作って、foo.so と一緒にプリロードさせれば OK です。LD_PRELOAD 環境変数にはスペース区切りで複数の共有オブジェクトを指定できます。

unsetenv() の代わりに、 setenv("LD_PRELOAD", ...) で LD_PRELOAD 変数を書き換えれば、子プロセスには別の共有オブジェクトをプリロードさせるという凝ったこともできます。

まとめ

子プロセスに LD_PRELOAD を継承させない方法を紹介しました。このようなことをしたい場面はほとんどないと思いますが、LD_PRELOAD で遊ぶ際には知っておくと役に立つかもしれません。 LD_PRELOAD は手軽なのでついついしょうもないことに乱用してしまいがちです。