無限遠まで突撃中

ネット日記書きの徒然。

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_tagDT_NEEDED タイプのエントリには必要な共有ライブラリ情報が格納されており、 ld-linux.so はこれを読んで共有ライブラリをロードするわけです。 ちなみに DT_NEEDED タイプのエントリの d_un には文字列テーブルのオフセットが記されています。 このオフセットからテーブルを読むとnull終端された文字列が手に入ります。

文字列はどこ

文字列テーブルと言いましたが、これはどこにあるのでしょうか。

これも .dynamic セクションにあって、 DT_STRTAB タイプのエントリを読むと d_un の値が文字列テーブルのアドレスとなっています。

これで無事に共有ライブラリの名前を手に入れた ld-linux.soファイルシステムから共有ライブラリを読み込むわけですね。

ダイレクトマーケティングコーナー

これいいっすよ。

追記は

またします。