OS Xにおける共有ライブラリについてのメモ

最近Z3のインストールに複数の意味でハマっていて、その過程で動的ライブラリに対する理解が深まったのでメモしておく。
autotaker.hatenablog.com

動的ライブラリとは

動的ライブラリは静的ライブラリと異なり、実行時にリンクされる。
今まで誤解していたのだが、ライブラリを使用する側からはライブラリが静的か動的かは関係がない。
コンパイラソースコードをオブジェクトファイルにする段階ではライブラリのヘッダファイルさえあればよくて、リンカが実行形式ファイルを出力する段階で初めてシンボルを静的に解決するか動的に解決するかが決まる。

覚えておくと便利なコマンド

環境はOS Xを想定している。

  • 実行形式ファイルあるいは動的ライブラリからそれが参照する動的ライブラリの一覧を取得する。
otool -L ファイル
  • 実行形式ファイルあるいは動的ライブラリからload commandsの一覧を取得する。
otool -l ファイル
  • 動的ライブラリがexportしている関数の一覧を取得する。
gobjdump -T ファイル

動的リンクの仕組み

ところで、動的リンカはどのようにして実行時に動的ライブラリの実体を見つけてくるのだろうか?

例えば、手元にある適当な動的ライブラリが使用する動的ライブラリを見てみよう。

/Users/autotaker/.cabal/lib/x86_64-osx-ghc-7.10.2/z3-4.1.0-CQEXZ6rXqZX8ei09CJh4x5/libHSz3-4.1.0-CQEXZ6rXqZX8ei09CJh4x5-ghc7.10.2.dylib:
	@rpath/libHSz3-4.1.0-CQEXZ6rXqZX8ei09CJh4x5-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	libz3.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSmtl-2.2.1-2BzSpTumj4ZLLPrpLUWDdr-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHStransformers-0.4.2.0-3eG64VdP2vzGjP6wJiCp5X-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHScontainers-0.5.6.2-LKCPrTJwOTOLk4OU37YmeN-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSdeepseq-1.4.1.1-LbCWUlehDDeLxurARKDH5o-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSarray-0.5.1.0-E0sTtauuKsGDLZoT7lTbgZ-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSbase-4.8.1.0-GDytRqRVSUX7zckgKqJjgw-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSinteger-gmp-1.0.0.0-2aU3IZNMF9a7mQ0OzsZ0dS-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libHSghc-prim-0.4.0.0-8TmvWUcS1U1IKHT0levwg3-ghc7.10.2.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)

このライブラリではライブラリの指定の仕方に以下の3種類が用いられている。(一般にはもう何種類かあるらしいが説明は割愛)

  • 絶対パス(例:/usr/lib/libiconv.2.dylib )
  • 相対パス(例:libz3.dylib)
  • @rpathで始まるパス(例:@rpath/libHSz3-4.1.0-CQEXZ6rXqZX8ei09CJh4x5-ghc7.10.2.dylib)

この書き方にしたがって実行時に動的ライブラリを探してくる仕組みが異なる。

絶対パスの場合

この場合は特に難しいことはなくそのパスで指定された動的ライブラリをリンクする。

相対パスの場合

この場合はまず、DYLD_LIBRARY_PATHという環境変数に指定されたパスからライブラリを探索する。
そこで見つからなかった場合は、DYLD_FALLBACK_LIBRARY_PATHを探索する。DYLD_FALLBACK_PATHのデフォルト値は$(HOME)/lib:/usr/local/lib/:/lib/:/usr/libとなっている。

@rpathの場合

実行ファイルおよび動的ライブラリにはrpathと呼ばれる、実行時に動的ライブラリを探索するパスのリストが埋め込まれている。動的リンカはそのリストに含まれるパスから動的ライブラリを探索する。

rpathの追加

ldコマンドに-rpath というオプションを渡すと、実行ファイルにそのパスがrpathとして埋め込まれる。
clangでコンパイルする場合は-Wl,-rpath -Wl,とする。

install name

そもそも上記の3種類の指定はどのように決定されるのか?
その答えがinstall nameと呼ばれるものである。動的ライブラリを生成するときにldに-install_name というオプションを渡すことで指定できる。あるいは、install_name_toolというコマンドで生成された動的ライブラリのinstall nameを後から変更することができる。
ldが実行ファイルを生成するとき、動的ライブラリを見つけてきて、そのライブラリのinstall nameを実行ファイルの動的ライブラリのパスとして埋め込む。

以上調べたことをまとめて、実際にいろいろ試せるように共有ライブラリのビルドを行うレポジトリをgithubに公開しておいた。
github.com

OS Xにおける動的リンクのまとめは以下のサイトを参考にした。
mikeash.com: Friday Q&A 2009-11-06: Linking and Install Names