基本的なELFの構造が把握できた

一日でELFの解析が終わるとは思っていなかった。
前に書いたが半月と読んでいたが、こんなに早く終わるとは。


この前調べたときよりもかなりいい感じで理解が進んだ。
やはり体を動かして、実際にELFの構造をダンプするプログラムを作る事は
ELFの理解に大いなる影響を及ぼしてくれた。


まず前回解説したELFの構造について多少の間違いがあった。
ええっとELFヘッダの後にすぐプログラムヘッダが来るという保障が無い。
しかしELFヘッダにはプログラムヘッダの開始位置、
あるいはセクションヘッダの開始位置が格納されているのでご安心を。
さらにプログラムの開始位置もちゃんとプログラムヘッダには記載されているため、
ELFヘッダ→プログラムヘッダ、そしてELFヘッダ→セクションヘッダと進む事で、
全てのELF構造、プログラムの位置などが分かる。


今回理解したのはプログラムのロードをどうやって行うか。
未だに固定アドレスを0x80000000以上という高位な位置に固定している
意味は分からないが、とりあえず固定アドレスを0x00000000に指定することで解決している。


さて、頭の中の整理も兼ねて、ここにELFの構造を書いてみる。


まずELFには様々なセクションという区分けがある。
例えば.textであれば実行可能コード、.rodataは読み取り専用データ、
.dataスタティックなデータなど)が収められる。


ELFの構造を解析すると、.textと.rodataは同じページに、
そして.dataあるいはまだ他にデータ領域があればそれらを別のページに収める構造になっていることが分かった。


と言うのも、ELF検証テストプログラムの解析結果を見れば一目瞭然なのである。
以下にELF検証テストプログラムの一部分のコードを示す。
mov dword [ebp-0x10],0x76
このコードは0x76にあるデータ(コードの開始から0x76byte目のデータ)を
使用するためのものなのだが、
それじゃあアドレス0x76には何があるのかを見ると、
00000076 <.rodata>:
76: 68 65 6c 6c 6f push $0x6f6c6c65
7b: 2e 0a 00 or %cs:(%eax),%al


これはobjdumpの結果だが、68、65、6c、6c…とあるだろう。
実はこれは"hello.\n"の文字コードである。しかもセクションはrodataである。
つまりコードと同じページに配置されることを意味するのである。
さらにいえば、ELF形式のファイルから、コードとこのrodataまでを
そのまま読み込んでメモリに格納する事でコードは実行できるはずである。


さて問題はdataセクション、つまりスタティックなデータである。
この秘密を知るために逆アセンブリした結果を以下に示す。
mov edx,[0x1080]
ちょうどここでスタティックなデータを使用しているのであるが、
注目してほしいのは0x1080という値である。おおよそ4kBちょっとという値。
つまりコードが格納されているページからおおよそ4kBちかく離れている所にあるデータにアクセスしているのである。
1ページは4kBが基本である事を考えれば、勘でなんとなく分かってくる。


さて次にobjdumpの結果を載せる。
00001080 :
1080: 78 56 js 10d8 <__bss_start+0x54>
1082: 34 12 xor $0x12,%al

memとは私が付けたスタティック変数の名前である。
ちゃんとELFを解析すればここはセクションであることが分かっている。
つまり
00001080 :
1080: 78 56 js 10d8 <__bss_start+0x54>
1082: 34 12 xor $0x12,%al
である。


さて、注目すべきは0x1080と表示されているところ。
このデータは0x1080に存在すべきであることを示している。
まさにスタティックなデータにアクセスしているのである。


それじゃあELFファイル上ではrodataとmemの間は空白なの?
と思う人も居るかもしれない。要はなぜ0x1080にdataがあるのが分かるのか。
これは簡単で、セクションヘッダがELFファイルにはたくさんあり、
それぞれのセクション(.text、.rodata、.data…)についての詳細なデータが記載されている。
ファイル上のここからがこのセクションの始まりで、名前はこうで、セクションのサイズはこうだとセクションヘッダを解析すればすぐに分かる。
これは実際にプログラムを作った方が良く分かる。

さて最後にもう一つ。4kBは0x1000である。なぜdataセクションは0x1080なのか。0x0080は余計じゃねぇかと思うだろう。
これはELFの仕様書を見ると、ELFヘッダ(むしろコードが始まる直前までのデータ全て)をロードすることになっている。


さて長々と説明してきたが、結論としてはコードとrodata(読み取り専用データ)は同じページに、
そしてdata(スタティックなデータ)は別のページ、それも2つのページは連続している必要性があることが条件で、
ローダを作成すれば、とりあえず動くと思われる。


ってことで、プロセスを動作させるには最低でも4kB、スタティックなデータがあれば8kB以上は必要である事が分かる。


当然これは簡単なプログラムに関するELFの解析なので、
やり方次第では連続していないページも利用できるかもしれませんが、
基本的な解析の解説なので、ご了承願います。


さて、簡単な解析は完了した。
しかしページングの理解がそろそろ必要である事が分かった。


とりあえずページングを知らなくてもプロセスのロードは出来るので、
上で示したことの実証を行い、実証が出来たら、ページングの勉強へと移行しようと考えている。