🐧 Linux 総合学習プラットフォーム
C・ビルドツール ・ 上級

共有ライブラリの確認

Linuxの実行ファイルの多くは、共有ライブラリ(.so)を実行時に読み込む形でビルドされています。ldd ./prog と打つと、その実行ファイルがどの共有ライブラリに依存し、それぞれが実際にどのパスへ解決されているかを一覧できます。「not found」と表示されたら依存先が見つかっておらず、実行時エラーの原因になります。シンボル(関数や変数)の状況を見たいときは nm、より詳しい逆アセンブルや構造を見たいときは objdump を併用します。これらは配布された実行ファイルの素性を調べるときにも役立ちます。

Linuxの実行ファイルは、必要な機能をすべて自分の中に抱え込んでいるとは限らない。

むしろ多くの場合、printf のようなありふれた関数の実体は、実行ファイルの外にある共有ライブラリに置かれていて、プログラムは起動するたびにそれを読み込んで使う。

共有ライブラリは拡張子 .so で表され、shared object(共有オブジェクト)の略だ。

🔗
たとえ共有ライブラリは、住人みんなで使う共用の道具置き場。各家に道具を全部そろえるより、ディスクもメモリも節約できる。

同じライブラリを多数のプログラムで共用できるため、それぞれの実行ファイルにライブラリを丸ごと埋め込む場合に比べ、ディスクとメモリを大きく節約できる。

つまずき身軽さには代償がある。実行時に必要な .so が見つからないと、プログラムは起動できずにエラーになる。

だが、その身軽さには代償もある。実行時に必要な .so が見つからないと、プログラムは起動できずにエラーになるのだ。

この「外部の部品に依存して動く」という性質を理解し、依存関係を調べられるようになることが、実行ファイルの素性を見抜く鍵になる。

🔗 動的リンクと静的リンク

ライブラリの結びつけ方には2通りある。

動的リンク実行ファイル.so共有本体は小さく、起動時に読み込む静的リンク実行ファイル.a を埋め込み単体で動くがサイズは大きい

1つは動的リンクで、共有ライブラリ(.so)を実行ファイル本体には含めず、起動時に動的リンカが読み込む方式だ。Linuxの実行ファイルの多くはこの形でビルドされており、本体は小さく保たれ、ライブラリは複数のプログラムで共有される。

もう1つは静的リンクで、ライブラリの実体を実行ファイルの中に埋め込んでしまう方式だ。この場合に使うのが静的ライブラリで、拡張子は .a である。

💡
ポイント動的リンクは .so を起動時に読み込み本体は小さい。静的リンクは .a を埋め込み単体で動くがサイズは大きい。

静的リンクした実行ファイルは外部の .so に依存せず単体で動く反面、サイズが大きくなり、ライブラリを更新しても作り直すまで反映されない。

組込みの分野では、依存を減らして配布を簡単にするためにあえて静的リンクを選ぶこともあり、両者は目的に応じて使い分けられる。

ある実行ファイルがどちらに頼っているのかを、どうやって知るのかを次に見る。

🔍 依存ライブラリを調べる ldd

ある実行ファイルが、どの共有ライブラリに依存しているかを調べるコマンドが ldd だ。

ldd ./prog ……必要な .so の一覧と、それぞれが実際にどのパスへ解決されているかを表示する。

ldd ./prog のように実行ファイルを渡すと、それが必要とする .so の一覧と、それぞれが実際にどのパスへ解決されているかが表示される。

出力は libm.so.6 => /lib64/libm.so.6 のような形で、左が必要としているライブラリ名、矢印の右が実際に見つかった場所だ。

これを見れば、プログラムが裏でどんな部品に支えられて動いているかが一目で分かる。

たとえば数学ライブラリを使うプログラムなら libm が、暗号を使うなら libssl や libcrypto が一覧に現れる。共有ライブラリの依存関係を確認したいときの第一手が、この ldd だ。

⚠ つまずいたら、まずここ not found が出たとき

ldd の出力で最も注意すべきは not found という表示だ。

つまずきldd の右側が => not found なら、その依存先が見つかっていない。起動時に「shared libraries を開けない」エラーになる。

あるライブラリ名の右側が => not found となっていたら、その依存先がシステム上で見つかっていないことを意味する。

この状態のままプログラムを起動しようとすると、「共有ライブラリを開けない(error while loading shared libraries)」という実行時エラーになって起動に失敗する。

原因の多くは、必要なライブラリがインストールされていない、あるいはインストールされてはいるが動的リンカの探索パスに含まれていない場所にある、というものだ。

対処としては、不足しているライブラリのパッケージを導入する、ライブラリの置き場所を探索パスに加える(設定ファイルや環境変数で指定し、必要に応じてキャッシュを更新する)といった方法がある。

コツ「動かないと思ったら、まず ldd で not found が無いか確認する」が定石。

「動かないと思ったら、まず ldd で not found が無いか確認する」が定石だ。

🔣 ライブラリの中身を調べる nm

ライブラリやオブジェクトファイルの「中身」、すなわちどんな関数や変数が入っているかを調べたいときは nm を使う。

nm は、オブジェクトファイル(.o)や実行ファイル、ライブラリに含まれるシンボル(関数名や変数名)の一覧を表示するコマンドだ。

💡
ポイントnm のシンボル記号で、T はそのファイル内で定義された関数、U は未定義(外部から与える必要あり)を表す。

各シンボルには種別を表す記号が付き、たとえば T はそのファイル内で定義されている関数(テキストセクション)、U はこのファイルでは未定義で外部から与えられる必要があるシンボルを表す。

これがリンクの謎解きに効く。リンク段階で undefined reference エラーが出たとき、nm で関係する .o を調べ、「その関数が U(未定義)のまま残っていないか」「定義しているはずの .o に T として存在するか」を突き合わせると、どこで実体が欠けているのかを切り分けられる。

シンボルの過不足を確認するための地味ながら強力な道具だ。

🛠 機械語の中まで見る objdump と ar

より深く、機械語のレベルまで踏み込んで調べたいときは objdump が役立つ。

objdump -d ……機械語を人間が読めるアセンブリへ戻す逆アセンブル。各セクションやヘッダの内容も確認できる。

objdump は実行ファイルやオブジェクトファイルの内部構造を詳しく表示するツールで、機械語を人間が読めるアセンブリへ戻す逆アセンブル(objdump -d)や、各セクションの情報、ヘッダの内容などを確認できる。

コンパイラがどんな命令を生成したかを確かめたいときや、低レベルの挙動を追うときに使う。

また、複数のオブジェクトファイルをまとめて1つの静的ライブラリ(.a)を作ったり、その中身を一覧・取り出したりするのが ar(archiver)だ。ar は .a アーカイブの作成・更新・内容確認を担い、自前のライブラリを用意するときに使う。

これらは普段の開発で毎回使うものではないが、いざというとき実行ファイルやライブラリの内部に分け入るための専門工具として知っておく価値がある。

🧰 これらの工具を抜くとき

これらのツールが活きるのは、トラブル対応と素性調査の場面だ。

配布されたバイナリや、ビルドはできたのに実行時に落ちるプログラムに出くわしたとき、まず ldd で依存ライブラリと not found の有無を確かめ、リンク段階の謎には nm でシンボルの過不足を、より深い挙動の調査には objdump で中身を見る、という流れが基本になる。

組込みLinuxでは、ターゲット機材に必要な共有ライブラリが揃っているか、依存が解決できるかが移植や配置の成否を左右するため、ldd による依存確認は特に重要だ。

「実行ファイルは外部の部品に依存して動く」という前提に立ち、その依存関係(ldd)・シンボル(nm)・内部構造(objdump)を調べる手段を持っておけば、ブラックボックスに見える実行ファイルの問題にも、根拠を持って立ち向かえるようになる。

この項目に出てくる用語

共有ライブラリきょうゆうらいぶらり
実行時に読み込まれる、複数プログラムで共用できるライブラリ(.so)。
オブジェクトファイルおぶじぇくとふぁいる
ソースを機械語へ変換した、リンク前の中間ファイル(.o)。
実行ファイルじっこうふぁいる
そのまま起動して動かせる、完成した機械語のプログラム。

関連コマンド

lddnmobjdumpar

▶ 学習アプリでこの続きを学ぶ・演習する