Linux では環境変数 LD_LIBRARY_PATH よりバイナリ埋め込みの RPATH が優先される

システムにインストールされた共有ライブラリの別バージョンをテスト用にどこかに入れて,そこを LD_LIBRARY_PATH で指定して動作確認することはよくあると思う。

ところが今回それをしようとしたがどうしても LD_LIBRARY_PATH で指定したものが使ってもらえない。LD_PRELOAD は行ける。

調べてみると,Linux では,(1) バイナリの DT_RPATH(DT_RUNPATH がなければ)→(2) 環境変数 LD_LIBRARY_PATH→(3) バイナリの DT_RUNPATH→……という順序で実行時のライブラリの検索がなされるようだ(ld.so(8))。ふつう環境変数などで実行時に何かを指定するのは既定値を置き換えたいからなのに,それより優先されるなんて DT_RPATH は何のためにあるんだろう。と思ったら DT_RPATH は deprecated らしい。まあ歴史的な事情があるのだろう。

で,GNU ld に -rpath=... を指定するとその値は DT_RPATH だけに埋め込まれる。同時に --enable-new-dtags も指定すると DT_RPATH と DT_RUNPATH の両方に埋め込まれる(で,前述のとおり実行時の検索では DT_RUNPATH があれば DT_RPATH は無視される)。そうしてつくったバイナリを objdump -p するとたしかに RPATH と RUNPATH が存在し,おなじ値を持っている。で,たしかに LD_LIBRARY_PATH が効くようになった。

ちなみに APR-util は configure の引数に LDFLAGS=-Wl,--enable-new-dtags を指定しても無視されたが,make 時の環境変数で指定したら行けた。こいつはリンクに APR 付属の libtool を使っているので,APR のビルドのときに --enable-new-dtags を指定していればこれが既定値になるのかもしれない。

しかし自分でビルドしたアプリを標準の場所以外に入れてその場所をバイナリの RPATH に埋め込むというスタイルはずっと前からやっていて,LD_LIBRARY_PATH もずっと前から使っているのに,どうして今になって問題が起きたんだろう。と思って調べると,つい最近まで使っていた NetBSD は LD_LIBRARY_PATH→DT_RPATH→……という順番らしい(ld.elf_so(1))。うん,やっぱりそういう設計が正解だと思う。

::2012-02-09 {unix}

update : 2012/02/09 (Thu) 18:57:14