Load 云々確認メモ
ロードというよりは共有ライブラリというか ld.so がヤッてる仕事を理解できれば、と思いつつ Linkers & Loaders 確認してみましたメモです。
以下、順不動で諸々メモ。
ええと、確認したのは
- 3 章 オブジェクトファイル
- 8 章 ロードとオーバレイ
- 9 章 共有ライブラリ
- 10 章 動的なリンクとロード
のあたりですが、基本的に ELF 限定で確認してたりしてます。
a.out のオブジェクトについて
何故か a.out について結構な量の補足説明があります。やっぱこれも ELF を知るには a.out を知っておけ的な何かがあるのでしょうか。a.out を load する手法についても
- NMAGIC
- ZMAGIC
- QMAGIC
というものが紹介されています。
v6 UNIX な exec で a.out な実行ファイルをメモリに展開する手法は NMAGIC と呼ばれるみたい。ちなみに Linkers & Loaders にて a.out のマジックナンバーに関する記述発見。以下、Linkers&Loaders 52p の原注 1 より引用。
歴史的に、オリジナルの PDP-11 におけるマジックナンバは 8 進表現で 407 だった。この値は、ヘッダの次の 7 ワードをスキップしてテキストセグメントにジャンプする分岐命令である。
Linkers & Loaders より引用
ELF
ELF のプログラム (セグメント) ヘッダとセクションヘッダの件。
- リンカが使用するのはセクションヘッダテーブル
- ローダが使用するのはプログラム (セグメント) ヘッダテーブル
ええと、x86-64 な環境で /bin/ls の readelf を材料にしてるんですがプログラムヘッダテーブルにあるオブジェクトの Type な属性のみ列挙してみたのが以下 (番号は自分で付けました)。
0 PHDR 1 INTERP 2 LOAD 3 LOAD 4 DYNAMIC 5 NOTE 6 GNU_EH_FRAME 7 GNU_STACK 8 GNU_RELRO
セクションヘッダテーブルにあるオブジェクトも同様に列挙してみます (一部のみ)。
[ 0] NULL [ 1] .interp PROGBITS [ 2] .note.ABI-tag NOTE [ 3] .note.gnu.build-i NOTE [ 4] .gnu.hash GNU_HASH [ 5] .dynsym DYNSYM [ 6] .dynstr STRTAB [ 7] .gnu.version VERSYM [ 8] .gnu.version_r VERNEED [ 9] .rela.dyn RELA [10] .rela.plt RELA [11] .init PROGBITS [12] .plt PROGBITS [13] .text PROGBITS [14] .fini PROGBITS [15] .rodata PROGBITS [16] .eh_frame_hdr PROGBITS [17] .eh_frame PROGBITS [18] .ctors PROGBITS [19] .dtors PROGBITS [20] .jcr PROGBITS [21] .dynamic DYNAMIC [22] .got PROGBITS [23] .got.plt PROGBITS [24] .data PROGBITS [25] .bss NOBITS [26] .gnu_debuglink PROGBITS [27] .shstrtab STRTAB
readelf がすばらなのはプログラムヘッダの出力のあとに Section to Segment mapping なる出力があることですね (下記については整形してます)。
Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07 08 .ctors .dtors .jcr .dynamic .got
0 はヘッダで 7 はスタックですね。どうやら .interp はテキストセグメントに含まれてて shell script の
#!/bin/bash
みたいな扱いになっているのかどうか。
あと、セクションタイプについても記述があるので肝心な部分一部控えを。
- DYNSYM は動的リンク用のシンボルとのこと
- RELA は再配置用ベース値を再配置エントリ自体に格納する、とあります
- DYNAMIC は動的リンク情報とのこと
また、セクションの種類として .got は Global Offset Table、.plt は Procefure Linkage Table で動的にリンクに使われる、とあります。
まとめの項でいくつか情報が列挙されてますが略。
動的リンク
10.3 節では「動的リンクプログラムのロード」という題が付いているのですが、ここは実際のソースと対比させつつ確認した方が良いのかも、と思っているのですが、実際ソースの可読性てきにどうなのかな、と思っていたりしています。
で、ここでは上でも控えた GOT だの PLT だのなソレが連呼されております。
- GOT は共有ライブラリ毎に存在
- プログラムが参照する静的なデータを指すポインタが格納される
- 動的リンカは GOT に格納されている全てのポインタを解決
あ、PLT というのは手続きの、なんだ。(PLT は、データのための GOT と同様の間接参照レベルを関数の呼び出しについても実現する(Linkers & Loaders より参照) とありますね。
ええと、ELF の動的リンクファイルの .dynsym というセクションではインポートおよびエクスポートなシンボルが格納されているとのこと。ええと、.dynstr とか .hash (上だと .gnu.hash?) はそれぞれシンボルの名前を示す文字列と実行時リンカ (って何でしょ) がシンボルを高速に検索するために使うハッシュテーブルが格納、とあります。これらも readelf な出力で確認できています。
また、.dynamic なセクションも動的リンカが使うとの記述あり。
遅延評価
鵜飼さん提供の例のスライド (http://labs.gree.jp/Top/Study/20061024/Report.html から download 可能) の 37 枚目のナニがようやくイメージできました。PLT の解決は実際に手続きが呼びだされるまでは保留になってて、二度目以降は cache されたオフセットと使って解決、ということになるのか。
このあたりを前提に例の読みづらいコードを確認してみます。