ttt

getttyent

(Unix系OSの掟) LD_LIBRARY_PATHは最後の武器だから、濫用してはいけない

2007-12-17 22:58:18 | デジタル・インターネット

たまに、~/.cshrcLD_LIBRARY_PATHの設定をしている人がいたりして、あらあら、この人は・・・っていう気分になることがあります。

LD_LIBRARY_PATH
は、リンクされる共有ライブラリの検索パスを強制的に変更するために使うものです。ただし、ふつうは、LD_LIBRARY_PATHを使わなくてもすむように、管理者が環境整備をしておくべきなんです。

LD_LIBRARY_PATHを設定したがために発生するトラブルってのが少なからずあって、

  • 本来リンクされるべきライブラリとは別のライブラリがリンクされてしまう
  • 一見うまく動いているように見えても、子プロセスにLD_LIBRARY_PATHが引き継がれてしまうために、子プロセスでトラブルが発生する

なんてのがありがちです。

一昨日の

pkgsrcでビルドしたfirefoxを別マシンの別ディレクトリにコピーして実行する

にて、LD_LIBRARY_PATHを使ったんですが、あのように、wrapperスクリプトを書いて、そこでLD_LIBRARY_PATHを設定するというように、LD_LIBRARY_PATHを使うにしても、できるだけ、LD_LIBRARY_PATHの有効範囲が狭まるように、配慮すべきなんですよね。

だから、~/.cshrcでLD_LIBRARY_PATHを設定するなんて、もう論外中の論外。

しかし、いくらwrapperスクリプトにしても、子プロセスに引き継がれてしまうので、気持ち悪い。なんかうまい方法はないだろうか、と考えていて思いついたのが、

バイナリファイル中のRUNPATHとかRPATHとかいうやつを書き換えてしまう

という方法。RUNPATHとRPATHの違いは実はよくわかってないんですけど(笑)、これは、LD_LIBRARY_PATH同様に、共有ライブラリの検索パスを指定するものらしく、環境変数ではなくて、各バイナリファイルごとに埋め込まれている情報です。

いまどきの実行可能なバイナリファイルは、ELFというフォーマットになっているそうなんですが、elfdumpやreadelfというコマンドで、RUNPATH、RPATHを読み出すことができます。

% elfdump -d /usr/pkg/lib/firefox/firefox-bin | grep PATH
      [31]  RUNPATH          0x362a            /usr/pkg/gcc34/lib:/usr/pkg/lib/firefox:/usr/pkg/lib
      [32]  RPATH            0x362a            /usr/pkg/gcc34/lib:/usr/pkg/lib/firefox:/usr/pkg/lib

% readelf -d /usr/pkg/lib/firefox/firefox-bin | grep PATH
0x0000001d (RUNPATH)                    Library runpath: [/usr/pkg/gcc34/lib:/usr/pkg/lib/firefox:/usr/pkg/lib]
0x0000000f (RPATH)                      Library rpath: [/usr/pkg/gcc34/lib:/usr/pkg/lib/firefox:/usr/pkg/lib]

ちなみに、ELFという共通フォーマットになっているためか、FreeBSD上で、Solaris/SPARC用のファイルの情報を見ることもできるようです。

このRUNPATH、RPATHを書き換えれば、LD_LIBRARY_PATHを設定しなくても、別ディレクトリにコピーしたバイナリコマンドが実行できるはず!

そう思って、検索してみて見つけた情報が、これ。

http://blogs.sun.com/ali/entry/changing_elf_runpaths
Changing ELF Runpaths (Code Included)

まさに、私がやりたかったこと、そのものが解説されているじゃないですか。非常にすぐれたドキュメントです。

・・・なのですが!!読み進んでいくうちに、ぎゃふん、と思うことに・・・

これ、OpenSolarisだけしか使えないテクニックみたいです。いや、一部、応用できるところもあるみたいですが。

斜め読みした程度での理解ですが・・・

  1. ELFバイナリに、RPATHやRUNPATHなどに設定される文字列が格納されている領域がある。
  2. その領域を書き換えれば、RPATH、RUNPATHの内容を変更できる。
  3. ただし、その領域のサイズは拡張できないので、もともと設定されている文字列の長さ以上は、埋め込むことができない。
  4. たとえば上記の例では、/usr/pkg/gcc34/lib:/usr/pkg/lib/firefox:/usr/pkg/lib となっているのを、/home/hogeuser/pkgsrc/usr/pkg/gcc34/lib:/home/hogeuser/pkgsrc/usr/pkg/lib/firefox:/home/hogeuser/pkgsrc/usr/pkg/lib にしたくても、文字列が元よりも長くなって収まりきらないので、不可能。
  5. そこでOpenSolarisでは、その領域に、はじめからある程度の余裕をもたせたよ。だから、あとから、ちょっとくらい長くなっても、追加できるようになりました。

みたいなことらしいです。

というわけで、Solaris8では、だめですね・・・

もうちょっとネット検索してみつけたのがこれ。

http://freshmeat.net/projects/chrpath/?topic_id=46

やってみた結果がこちら。

% ./chrpath -r /tmp:/hoge/tmp:/huga/huga/huga:/aaaaaaaaa/bbbbbbbbbbbb emacs
emacs: RUNPATH=/usr/pkg/xorg/lib:/usr/pkg/lib:/usr/sfw/lib
new rpath '/tmp:/hoge/tmp:/huga/huga/huga:/aaaaaaaaa/bbbbbbbbbbbb' too large; maximum length 43

このchrpathも、ELFの文字列のバッファを書き換えるだけのようで、サイズを拡張できない以上、元の長さ以上のパスへ変更することはできませんでした。

ふと思ったんですが、個人的な経験の中で、RPATH、RUNPATHを使うのって、Solarisだけのような気がしているんですが、そんなもんなんでしょうか。