Forth で ELF を作る
Forth
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

リンク集