簡易brainf*ckコンパイラを作る part2
part1に引き続き、もう一点必要なものがある。
brainf*ckはそれぞれの命令以外に、少なくともサイズ30000のバイトの配列を持つ必要があり、これは今までgasの記法を主に使っている所で見られた
.comm mem, 30000
に相当する。
この領域をELFファイルに直接持たせ、使用する必要がある。
目標
ELFファイルにbss領域を持たせ、その領域で値の読み書きをする。
コード
今までにあったELFとの違いは
- プログラムヘッダが2つある
- 実行コード部分でbss領域に書き込む処理を追加
- sectionヘッダーにbss領域についての記述を追加
といったところになる。
処理本体のコードではまず、bss領域のアドレスをrbxレジスタへ格納し(46行目)、その先頭アドレスへAsciiコードで"H"を表す0x48を格納している。
その後のwriteシステムコールを呼ぶ箇所で、出力対象文字列へのアドレス指定へbss領域を指定。最後に後始末としてexitシステムコールをよんで終了させている。
ここでrbxレジスタへ指定しているbss領域のアドレスは0x08248000で、この値はbss領域用のsectionヘッダーにて、sh_addrの値を$$ + 200000としている事により、orgディレクティブで指定している0x8048000から200000だけ進んだ位置がbss領域として確保されている為にこの値となる。
前述したように、bss領域についてsectionヘッダーを追加しており、また、bss領域はセグメントなのでprogramヘッダーも追加している。
ここでbss領域についてprogramヘッダーを定義する際にそれぞれの値をどう指定するかは、bss領域を使う単純なプログラムを書いて、そこから得られる実行ファイルのヘッダー情報を読み取って真似た。
例えば
$ cat bss.c int foo[200]; void _start(){ }
というようなbss.cを記述し
$ gcc -nostdlib bss.c $ readelf -all a.out
とすることで
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000001e8 0x00000000000001e8 R E 200000 LOAD 0x0000000000001000 0x0000000000601000 0x0000000000601000 0x0000000000000000 0x0000000000000320 RW 200000 NOTE 0x0000000000000158 0x0000000000400158 0x0000000000400158 0x0000000000000024 0x0000000000000024 R 4 GNU_EH_FRAME 0x0000000000000198 0x0000000000400198 0x0000000000400198 0x0000000000000014 0x0000000000000014 R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8
といった結果を得られる。同時に得られるsectionヘッダーの情報
[ 5] .bss NOBITS 0000000000601000 00001000 0000000000000320 0000000000000000 WA 0 0 32
より、bss領域のプログラムヘッダーは
LOAD 0x0000000000001000 0x0000000000601000 0x0000000000601000 0x0000000000000000 0x0000000000000320 RW 200000
であることが読み取れる。アドレスの開始位置とサイズが同じなので。
これらを参考に、ELFヘッダーの調整、programヘッダーの追加、sectionヘッダーの追加をすることでbss領域を確保することができる。
実行
$ nasm -f bin -o nasm.out elf64-bss.asm $ ./nasm.out H
期待する結果を得られ、また
$ readelf -all nasm.out ... Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .shstrtbl STRTAB 0000000000000000 000000e6 0000000000000016 0000000000000000 0 0 1 [ 2] .text PROGBITS 00000000080480b0 000000b0 0000000000000036 0000000000000000 AX 0 0 4 [ 3] .bss NOBITS 0000000008078d40 00001000 0000000000007530 0000000000000000 WA 0 0 16 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000008048000 0x0000000008048000 0x00000000000001fc 0x00000000000001fc R E 200000 LOAD 0x0000000000001000 0x0000000008248000 0x0000000008248000 0x0000000000000000 0x0000000000007530 RW 200000 ...
期待されるセグメントがbss領域として動作していると思われます