Forth で ELF を作る
2022-9-23 17:33 JST
gforth と swapforth の cross.fs をみながらELF を作ることにトライする。
バイナリのファイルを書く
Forth の拡張子って統一されてないよね。.fs か .4th が多いみたい。ここは gforth に習って .fs にしておく。
gforth の cross.fs の magic を習ってそう書いてみる。
bin.fs
create magic s" Gforth6x" here over allot swap move
0 value file
s" test.bin" w/o bin create-file throw
to file
magic 8 file write-file throw
file close-file throw
bye
とりあえず test.bin が出来た。いまは Gforth6x という文字列を書いただけなのでバイナリでなくてもよいが、これが byte でもいい。allot 下後に、開放していないのが気になるなら-8 allotすればいい。
struct を使う
次に struct を使ってみる。これを使うと構造体を作ることが出来る。Forth の 2012 には標準であるらしいが、昔の ANS Forth にはない。だから、みんなが勝手に作っている。ここでは gforth の struct を使う。
str.fs
struct
cell% field >magic
end-struct my-struct
: %allocerase ( align size -- addr )
dup >r %alloc dup r> erase ;
: define-my-struct ( addr -- struct-addr )
dup @ ?dup IF nip EXIT THEN
my-struct %allocerase tuck swap ! ;
variable obj
obj define-my-struct drop
obj @ >magic @ .
1234 obj @ >magic !
obj @ >magic @ .
bye
試しに書き出してみる
magic.fs
variable obj
obj define-my-struct drop
obj @ >magic @ .
hex
cafebabedeadbeaf obj @ >magic !
obj @ >magic @ .
0 value file
s" magic.bin" w/o bin create-file throw
to file
obj @ 8 file write-file throw
file close-file throw
$ od -tx4 magic.bin
0000000 deadbeaf cafebabe
0000010
ELF64 Header と PHeader を生成する
ということで Header と PHeader を設定してみる。まだ設定してないところがあったりして、まともなelf にはなっていない(壊れている)。プログラムもない。とはいえ、file コマンドで認識するくらいにはなった。
よくみると define-<構造体> が冗長。これ、マクロ化してそれをつくる word を登録すべきだよね。まぁこれはまた今度にしよう。
mkelf.fs
struct
align size
8 4 field >elf64-magic
1 1 field >elf64-ei_class
1 1 field >elf64-ei_data
1 1 field >elf64-ei_version
1 1 field >elf64-ei_osabi
1 1 field >elf64-ei_abiversion
1 7 field >elf64-ei_pad
2 2 field >elf64-e_type
2 2 field >elf64-e_machine
4 4 field >elf64-e_version
8 8 field >elf64-e_entry
8 8 field >elf64-e_phoff
8 8 field >elf64-e_shoff
4 4 field >elf64-e_flags
2 2 field >elf64-e_ehsize
2 2 field >elf64-e_phentsize
2 2 field >elf64-e_phnum
2 2 field >elf64-e_shentsize
2 2 field >elf64-e_shnum
2 2 field >elf64-e_shstrndx
end-struct elf64-struct
struct
align size
8 8 field >elf64-p_type
8 8 field >elf64-p_flags
8 8 field >elf64-p_offset
8 8 field >elf64-p_vaddr
8 8 field >elf64-p_paddr
8 8 field >elf64-p_filesz
8 8 field >elf64-p_memsz
8 8 field >elf64-p_align
end-struct elf64-pheader
: %allocerase ( align size -- addr )
dup >r %alloc dup r> erase ;
: define-elf64-struct ( addr -- struct-addr )
dup @ ?dup IF nip EXIT THEN
elf64-struct %allocerase tuck swap ! ;
: define-elf64-pheader-struct ( addr -- struct-addr )
dup @ ?dup IF nip EXIT THEN
elf64-pheader %allocerase tuck swap ! ;
variable elf64-hobj
elf64-hobj define-elf64-struct drop
elf64-hobj @ >elf64-magic @ .
elf64-struct . .
hex
0x464c457f elf64-hobj @ >elf64-magic !
2 elf64-hobj @ >elf64-ei_class !
1 elf64-hobj @ >elf64-ei_data !
1 elf64-hobj @ >elf64-ei_version !
2 elf64-hobj @ >elf64-e_type !
0x3e elf64-hobj @ >elf64-e_machine !
elf64-pheader . .
0x10000 constant vaddr
1 elf64-hobj @ >elf64-e_version !
vaddr elf64-hobj @ >elf64-e_entry !
elf64-struct nip elf64-hobj @ >elf64-e_phoff
variable elf64-phobj
elf64-phobj define-elf64-pheader-struct drop
1 elf64-phobj @ >elf64-p_type ! PT_LOAD
7 elf64-phobj @ >elf64-p_flags ! PF_X | PF_W | PF_R
vaddr elf64-phobj @ >elf64-p_vaddr !
decimal
128 1024 * elf64-phobj @ >elf64-p_memsz !
4 1024 * elf64-phobj @ >elf64-p_filesz !
write test64.elf
0 value file
s" test64.elf" w/o bin create-file throw
to file
elf64-hobj @ elf64-struct nip file write-file throw
elf64-phobj @ elf64-struct nip file write-file throw
file close-file throw
bye
$ file test64.elf
test64.elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), no program header, no section header
リンク集
ELF ヘッダーはここを見れば良い
プログラムヘッダーはここを見れば良い
「実際の実行には必要ないはずだからメモリに読み込む必要ないんじゃない? 実際のプログラムの部分だけを指定したいよね」と思うよね。 ページ境界の問題があるのかな?
scrapbox で Forth 関連をまとめている人がいるのね。
Thinking Forth を誰かが日本語訳している。
Thinking Forth 自体は公開されているみたいね。
CREATE DOES> の説明がある