lsso - 実行ファイルが必要とする共有ライブラリをチェックする(特にクロスコンパイル環境で)

目的

ある実行ファイルの動作に、どの共有ライブラリが必要かを把握したい。
ストレート環境であれば /bin/ldd を使えば良い(BINARY HACKS, HACK #7 を参照)のだが、クロス環境だと一々実行するのが面倒臭い。
一括で、必要なライブラリ一覧を得る方法は無いか。

結果

readelfを使って、elf の dynamic section から SONAME と NEEDED を抜き出して、依存関係を(再帰的に)把握するスクリプトを作成した。

lsso
https://sssvn.jp/svn/spikelet/utils/lsso

依存関係といえば有向グラフ、有向グラフといえばGraphviz(Graphviz - Graph Visualization Software)なので、Graphviz形式での出力も可能にした。

実行例は、詳細の最後の辺を参照。

以下、詳細。

背景と動機

いきなり狭い話ですが。
ロスコンパイル環境で、スタンドアローン実行できるように(要はROM化)しようとした時に、必要なコマンドやライブラリを選定する必要がある。
コマンドはまあ判断しやすいが、使われているライブラリを個々に確認するのが、面倒臭い。なんとかならないか。
実機上で ldd を使えば良い、のだけど、それも面倒なんです。確認もしにくいし。

方針

BINARY HACS(これはとても良い本だ)のHACK #7 「lddで共有ライブラリの依存関係をチェックする」を参考に、ELFの動的セクションに必要な情報が入っていることがわかった。

  • SONAME: shared object の「名前」
  • NEEDED: バイナリの実行に必要な shared object の SONAME

実行ファイルには、動的リンクしたいライブラリの「名前」をNEEDEDとして書いておき、ここの記述(と/etc/ld.so.confなど)を元にローダがSONAMEから実際のライブラリファイルを見つけてくる。

実行ファイルのNEEDED → SONAME → 実際のライブラリファイル

ここで、ライブラリファイルもNEEDEDを持っている可能性があるので、ライブラリの検索は再帰的に行う必要がある。

検索スクリプト

実行ファイルのNEEDEDから、実際に動的リンクされるライブラリファイルを見つけるスクリプトを作成した。

lsso
https://sssvn.jp/svn/spikelet/utils/lsso

"--verbose" オプションを付けると、個々のバイナリやライブラリの依存SONAMEや、細かいWARNINGを表示する。
"--graphviz" オプションを付けると、Graphviz(Graphviz - Graph Visualization Software)の dot 形式で出力する。

実行例

実行例1
(とあるクロス環境で)
% cd <some-root-of-cross-environment>
% export READELF_COMMAND=/usr/local/path/xxxx-readelf
% lsso usr/local/bin/bash
lib/ld-2.2.4.90-xxxx.so
lib/libc-2.2.4.90-xxxx.so
lib/libdl-2.2.4.90-xxxx.so
実行例2
(一応ストレート環境でも動く)
% cd /
% lsso bin/ls
lib/ld-2.3.4.so
lib/libacl.so.1.1.0
lib/libattr.so.1.1.0
lib/libc-2.3.4.so
lib/libpthread-0.10.so
lib/librt-2.3.4.so
lib/libselinux.so.1
実行例3
(Graphviz形式で出力)

注意:SVGの閲覧には、SVG閲覧プラグインなどが必要(Web Center Features - SVG - Manual Download)。閲覧中は、Alt+マウスカーソルで移動できます。

制限事項

クロス環境を意図しているので、いくつか使用上の注意がある。

  1. 実行は、環境のrootディレクトリで行うこと
    • 例えばクロス環境のルートディレクトリが /home/hoge/cross-c-root-path ならば、このディレクトリに移動してからスクリプトを実行する。
    • ストレート環境で実行する場合は / に移動してから実行する。
  2. クロス用のreadelfコマンドを使いたい場合は以下のいずれかで指定すること
  3. 共有ライブラリの検索パスではしょっている箇所がある
    • 以下の箇所:
      1. elf ファイル内の dynamic セクションの RPATH や RUNPATH (一応、WARNINGは表示する)
      2. 環境変数 LD_LIBRARY_PATH が指す場所
      3. /etc/ld.so.cache の中身
    • 変わりに、"--libpath" オプションで検索パスを指定する (--libpath=usr/X11R6/lib:lib など)

スクリプトを簡単にする都合だったり、そもそもクロス環境では把握できない情報だったりするので、この辺は勘弁。