2.共有ライブラリの正体 それでは,共有ライブラリの実体について調べてみましょう. そもそもhello.cはなぜ外部ライブラリが必要になったのでしょうか.コンパイル後に生成されるオブジェクトファイルを使い,その理由を調べてみましょう.先ほどのコマンドではオブジェクトファイルは生成されなかったので,-cオプションを用いてhello.oファイルを作成します. Cソースで定義される変数や関数は,オブジェクトファイル中では「シンボル」として管理されます.それぞれのシンボルには属性があり,4バイト整数を指すのか,関数のエントリアドレスを指すのかなどの情報が記録されています.オブジェクトファイルの時点ではアドレスは決定されませんが,リンカであるldコマンドがリンクを行うことで,シンボルへの最終的なアドレス割り付けが行われます. オブジェクトファイル中のシンボル一覧は,nmコマンドで閲覧することができます.シンボルにはグローバルスコープをもつものと,ローカルスコープをもつものの2種類があるので,通常は-g(Global)オプションを指定することで,グローバルシンボルのみを表示させます. リスト4でわかるように,hello.cをコンパイルして得られたhello.oオブジェクトファイル中には,二つのシンボルが含まれています.一つはmain,もう一つはprintfです.mainはmain関数のエントリアドレスを指していますが,シンボルタイプがTであることに注意してください.これはmainシンボルが.textセクションに存在することを意味しています.UNIXでは実行ファイル内部は複数のセクションから構成されており,実行コードは.textセクションに格納される決まりになっています.一方,シンボルprintfのタイプはUになっています.UはUndefinedの略で,該当オブジェクトファイル中でそのシンボルは定義されていないことを表しています.つまり,printf関数は外部に存在するというわけです.
それでは,printfはどこにあるのでしょうか.ある実行可能ファイルが「依存」しているファイルの一覧は,lddコマンドで参照することができます(リスト5).
lddコマンドによると,helloは/lib/libc.so.6および/lib/ld-linux.so.2に依存しています.二つのファイルは/lib/ディレクトリ中でシンボリックリンクとして作成されており,それぞれの実体はlibc-2.2.4.so,ld-2.2.4.soとなっています.libc-2.2.4.soはGNU C library(glibc)バージョン2.2.4の共有ライブラリ版,ld-2.2.4.soはglibc 2.2.4用のELF版ダイナミックリンカ・ローダです.libc-2.2.4.soは1Mバイトを越える巨大ライブラリファイルであることに注目しましょう. ダイナミックリンカ・ローダとは,実行ファイルが必要とする共有ライブラリのセットアップを行い,ライブラリ関数アドレスの最終的な解決を行うためのプログラムです.現在,ダイナミックリンカ・ローダには実行可能ファイル形式に応じてa.out用およびELF(Executable and Linking Format)用の2種類が存在します(比較的最近までUNIX界ではa.out形式が一般的だったが,現在はほとんどがELFへと移行している). ファイル形式上,ld-2.2.4.soは共有オブジェクトファイルということになっていますが,じつはその正体は「インタプリタ」です.ELFファイルの解析ツールreadelfに-lオプションを指定し,hello中のプログラムヘッダと呼ばれる構造を表示させてみましょう(リスト6).
プログラムヘッダ中に存在するインタプリタ情報(INTERP)を見てください.プログラムインタプリタとして/lib/ld-linux-so.2を呼び出すように設定されていることがわかります.helloを実行すると,まず最初にプログラムインタプリタ/lib/ld-linux-so.2が起動され,hello内部で未定義になっているprintf関数のエントリアドレスが解決されます.そのうえで初めてhelloコードが実行されるのです.この状況を実際に自分の目で確かめてみましょう.strace(System call TRACE)コマンドは指定されたプログラムの実行状況をシステムコールレベルで追跡するためのものです(リスト7).
以降の内容は本誌を参照ください
今月号特集トップページへ戻る Copyright 2002 西田 亙 |