共有ライブラリロードに使われるRPATHとRUNPATH

Linux向けにC/C++でアプリケーションを実装していると、共有ライブラリ読み込み時のライブラリ検索パスに悩まされることがあります。 ldconfigコマンドや環境変数LD_LIBRARY_PATHで回避したりすることもあるのですが、今回は実行形式や共有ライブラリに直接パスを埋め込めるrpathとrunpathを 使ったので、実際使った場合の挙動を確認してみました。

# そもそもrpath/runpathってなに?どういうとき使うのか?

実行形式(ELFファイル)に直接埋め込んで指定できる、共有ライブラリの検索パスです。 埋め込まれているので、その実行形式を使う側は共有ライブラリへのパスを指定する必要がなくなります。

runpath指定
デフォルト
アプリケーション
/usr/local/xxx/lib/libXXX.so
/usr/local/lib/libxxx.so

たとえば、独自のアプリケーションで共有ライブラリをそれを使用する実行形式を/usr/local/xxx/{bin,lib}に配置したい場合、 rpath/runpath無しだと、/etc/ldconfig.confでパスを指定したり、環境変数LD_LIBRARY_PATHでパスを指定してあげる必要があります。
その場合、それ以外の実行形式を動かす場合にも影響が出てしまうため、もし/usr/local/lib/usr/local/xxx/libに同じライブラリがあると、読み込まれるライブラリが違ったことで問題が発生する可能性もあります。

# どうやって埋め込むの?確認方法は?

RUNPATHは実行形式のリンク時にリンカのオプションで指定できます。GCCを使っているなら-Wl,-rpath,{PATH}です。 {PATH}の部分に任意のパスを指定します。

$ gcc uselib.c -o uselib_runpath -Wl,-rpath,./somelibdir

そしてreadelfコマンドで確認できます。

$ readelf -d ./uselib/uselib_runpath | grep path
 0x000000000000001d (RUNPATH)            Library runpath: [./somelibdir]

RPATHは、実は非推奨なようです。(-Wl,--disable-new-dtagsというオプションでRUNPATHからRPATHに切り替えできます)

$ gcc uselib.c -o uselib_rpath -Wl,-rpath,./somelibdir -Wl,--disable-new-dtags
$ readelf -d ./uselib/uselib_runpath | grep path
 0x000000000000000f (RPATH)              Library rpath: [./somelibdir]

なので新規にビルドするなら普通に-Wl,-rpath,./somelibdirだけ指定すればOKです。

これらを指定するときのパスは絶対パスでも相対パスでもOKな用です。 相対パスなら生成された実行形式を実行したときのカレントディレクトリが基準となります。 (これはあまり使い勝手よくなさそうですが)

それと特殊な指定方法として、$ORIGINを使えるようです。 $ORIGINは、生成された実行形式の実行時に、そのファイルパスに展開されます。 基本的にはこの$ORIGINを使って、$ORIGIN/../libとすれば、冒頭の/usr/local/xxx/bin/に配置された場合にも対応できるはずです。

これらの挙動を確認するサンプルプログラムを用意しておきました。 kenyog/example_rpath_runpath (opens new window)

# CMAKEでの設定方法は?

CMAKEを使う場合はもっと簡単でした。以下のcommandを指定すればOKです。

set_target_properties(target PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")

# 結局rpath/runpath/LD_LIBRARY_PATHは何が違うの?

結論としては優先順位が違うようです。 参考サイトによると、以下の順序でした。

  1. rpath
  2. LD_LIBRARY_PATH
  3. runpath
  4. システム設定のパス(/etc/ldconfig.conf)

実際これを確認するため、上記のサンプルプログラムではmake runすると2つのライブラリを呼び分けを確認できます。 実行結果 (opens new window)

rpathが非推奨となっているのは、LD_LIBRARY_PATHより優先されてしまうことが理由のようです。 ユーザーが意図的に読み込み先を変更する手段は残しておきたいということのようですね。

# 参考サイト

実際に試してみるうえで、以下のサイトを参考にしました。 ものすごい詳細に実行結果も書いてくれてるので、非常に勉強になりました。

rpath vs runpath (opens new window)