ld-linux.soと共有ライブラリあたりの雑記
2020/09/28 .interp
と動的リンカの部分を追記しました
最近ブログ更新していなかったので、最近知ったこと書きます。
あ、 ld-linux.so
とか共有ライブラリとかの概略はしません。
この記事読んでいる人はわかっていると思うので。
.interp
と動的リンカ
ELF実行形式には .interp
と呼ばれるセクションがある場合があります。
これは動的リンカを指定するためのセクションで、動的リンカがあるパスが文字列として埋め込まれています。
カーネルはこの .interp
セクションを見て動的リンカ( ld-linux.so
など)をロードします。
実行形式のロードとセグメント
ld-linux.so
はELFのプログラムヘッダを読んでセグメントをメモリ上に配置していきますが、プログラムヘッダにセクション情報はありません。
動的リンク動的ロードが必要な共有ライブラリの情報は .dynamic
セクション にありますが、どうやってこのセクションがあるセグメントを特定しているのでしょうか。
実はセグメントの中には DYNAMIC
と呼ばれる種類のセグメントがあり、プログラムヘッダからセグメント種別を見分けることができます。
Hello, World!プログラムをコンパイルした実行ファイルを readelf -l
してみてみると次のようになっています。
$ readelf -W -l hello Elf file type is DYN (Shared object file) Entry point 0x1040 There are 11 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8 INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000558 0x000558 R 0x1000 LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0001d5 0x0001d5 R E 0x1000 LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000120 0x000120 R 0x1000 LOAD 0x002de8 0x0000000000003de8 0x0000000000003de8 0x000248 0x000250 RW 0x1000 DYNAMIC 0x002df8 0x0000000000003df8 0x0000000000003df8 0x0001e0 0x0001e0 RW 0x8 NOTE 0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R 0x4 GNU_EH_FRAME 0x002014 0x0000000000002014 0x0000000000002014 0x000034 0x000034 R 0x4 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x002de8 0x0000000000003de8 0x0000000000003de8 0x000218 0x000218 R 0x1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 03 .init .plt .text .fini 04 .rodata .eh_frame_hdr .eh_frame 05 .init_array .fini_array .dynamic .got .got.plt .data .bss 06 .dynamic 07 .note.gnu.build-id .note.ABI-tag 08 .eh_frame_hdr 09 10 .init_array .fini_array .dynamic .got
確かに DYNAMIC
タイプのセグメントがあります。
セクションとセグメントのマップ情報を見ても、 DYNAMIC
セグメントには .dynamic
セクションが入っています。
DYNAIMC
セグメントを読みに行けば .dynamic
セグメントが解析できるわけですね。
.dynamic
セクション
.dynamic
セクションのエントリは次のようになっています。
typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn; extern Elf64_Dyn _DYNAMIC[];
d_tag
で情報の種類を、 d_un
で情報本体を表します。
d_tag
が DT_NEEDED
タイプのエントリには必要な共有ライブラリ情報が格納されており、 ld-linux.so
はこれを読んで共有ライブラリをロードするわけです。
ちなみに DT_NEEDED
タイプのエントリの d_un
には文字列テーブルのオフセットが記されています。
このオフセットからテーブルを読むとnull終端された文字列が手に入ります。
文字列はどこ
文字列テーブルと言いましたが、これはどこにあるのでしょうか。
これも .dynamic
セクションにあって、 DT_STRTAB
タイプのエントリを読むと d_un
の値が文字列テーブルのアドレスとなっています。
これで無事に共有ライブラリの名前を手に入れた ld-linux.so
はファイルシステムから共有ライブラリを読み込むわけですね。
ダイレクトマーケティングコーナー
これいいっすよ。
追記は
またします。