最小の実行ファイルを眺めてみる
実行ファイルを自力で生成するのは全プログラマの夢だと思う(要出典)
ELFを生成すればいいのだが、そこそこ複雑な上種類も割とあってしんどい
標準だとダイナミックリンクされるが、ローダーが絡んできて複雑
スタティックリンクされたバイナリやRelocatableなどは単純なので、最初はここから始める
天才であれば仕様書眺めただけでコンパイラを作成できるのだろうが、僕は凡人なのでコンパイラの出力を眺めることにする
実行ファイルを出力するだけならコンパイラは別に必要はないので今回は使わない
x86-64のLinux環境でこのようなアセンブリを使う
これはただexitシステムコールを呼ぶだけのもの
code:exit.s
.globl _start
_start:
movq %rax, %rdi
movq $60, %rax
syscall
実はプログラムの実行時、mainは直接呼ばれていない
Linux環境では_startが呼ばれる
本来であれば_startの中でスタック領域に書き込まれた引数等をレジスタに置き直したりしているが今回は本筋ではないのでやらない
この状態から実行ファイルを作るにはアセンブラとリンカが必要
今回はLinux環境であればどこにでもあると思われるBinutilsを使用する
それぞれasとld(実体はld.bfdだが普通の環境だとldにリンクされている)
眺めるツールとしてBinutils付属のobjdumpも使う
Relocatableはas -o exit.o exit.sを実行すると生成される
objdump -d exit.oするとバイナリダンプ付きの逆アセンブル結果が出てくる
_startは.textセクションに属することがわかる
適当なツールでダンプするとこのバイナリは0x40に配置されていることがわかる
objdump -x exit.oをするとセクションヘッダをダンプしてくれる。
.textのFile offは00000040になっており上のダンプ結果と一致していることがわかる
余談だがlld(LLVMのリンカ)でリンクすると機械語領域の後続が結構な範囲0xcc(x86-64ではデバッガの割り込み命令)で埋められてて頭いいなと思った
実行ファイルは先程のRelocatableをリンクすることで生成される
ld -static -o exit exit.oを実行する
objdump -d exitをすると.textセクションの位置が401000に移っているのがわかる(環境によって違うかもしれない)
objdump -x exitのStart Addressも0x0000000000401000になっている
プログラムヘッダーの二つ目のvaddrも0x0000000000401000になっている
これは仮想メモリアドレスで、Linuxのローダーが実行ファイルを読み込んだ時のアドレスを示している
適当なツールでダンプしてみるとファイル上の位置も0x1000に移っているのがわかる
これもプログラムヘッダーの二つ目のoffやセクションヘッダーのFile offと一致している