2024-08-09

「低レイヤを知りたい人のためのCコンパイラ作成入門」第二回勉強会まとめ

勉強した範囲

  • macOSでCコンパイラを作成するための開発環境の構築
  • Cとそれに対応するアセンブラ〜アセンブリプログラムの実行

所感&メモ

  • WindowsはWindows Subsystem for Linux(WSL)というアプリケーションをインストールすることで、Linux互換環境を容易に準備できる。
  • macOSはLinuxとアセンブリのソースレベルでは完全互換ではないため、仮想環境を使ってLinuxを用意したほうがいい。私はmacOSを使用しているので、Dockerで開発環境の構築を行った。
    Dockerを使用した理由: この勉強会の趣旨はCコンパイラ作成のテクニックを学ぶためのものだが、細かな点で非互換性に悩まされ時間を使うのは本末転倒であるため。

実行したコマンド

セットアップ

まず、Dockerfile からイメージを 構築(build)する。

docker build -t compilerbook https://www.sigbus.info/compilerbook/Dockerfile

buildが完了したので、lsコマンドでコンテナの中身を確認してみる。

docker run --rm compilerbook ls /

bin
boot
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

コンテナ内でシェルを起動させるコマンド

docker run -it -v $HOME/9cc:/9cc -w /9cc compilerbook

オプション説明

docker run -it -v [ボリューム(ホストOSのパス)]:[マウント先(コンテナ内のパス)] -w /[実行をするコンテナ内のディレクトリを指定]
オプション
-it コンテナ内でシェルを起動して、対話形式で使用する
-v 現在の作業ディレクトリをコンテナ内にマウント(ホストOSとコンテナ間でデータを共有)する。
-w 現在の作業用ディレクトリの中で実行されるように指示

C言語で書かれたプログラムを用意

〜/9cc/test1.c内に以下のプログラムを作成。

int main() {
  return 42;
}

int:42という整数を返すのでint型(ineger)を宣言。
main:全体を管理する機能を持った関数。プログラム内で最初に呼ばれる特別な関数。

コンパイル

コンピュータはC言語で書かれたプログラム(ソースプログラム)を直接実行することはできないので、直接実行可能な「機械語命令(アセンブリ言語)」に変換する必要がある。これをコンパイルという。

先ほどのソースプログラムtest1.cをコンパイルする。

docker run -it -v $HOME/9cc:/9cc -w /9cc compilerbook cc -o test1 test1.c
オプション
cc コンパイルの設定
-o 出力ファイルに (デフォルトの a.out の代わりに) <出力ファイル> という名前を付ける。

コンパイルによってできた実行ファイルが存在するか確認

test1という実行ファイルが作成されている。

docker run -it -v $HOME/9cc:/9cc -w /9cc compilerbook

user@db2d9edc013e:/9cc$ ls
9cc  test1  test1.c

test1の中身は、機械語命令に対応するアセンブリが表示されている。

user@93b91a9a80fa:/9cc$ objdump -d -M intel ./test1

./test1:     file format elf64-littleaarch64

Disassembly of section .init:

0000000000000580 <_init>:
 580:    objdump: unrecognised disassembler option: intel
d503201f     nop
 584:    a9bf7bfd     stp    x29, x30, [sp, #-16]!
 588:    910003fd     mov    x29, sp
 58c:    9400002a     bl    634 <call_weak_fn>
 590:    a8c17bfd     ldp    x29, x30, [sp], #16
 594:    d65f03c0     ret

Disassembly of section .plt:

00000000000005a0 <.plt>:
 5a0:    a9bf7bf0     stp    x16, x30, [sp, #-16]!
 5a4:    f00000f0     adrp    x16, 1f000 <__FRAME_END__+0x1e7f4>
 5a8:    f947d611     ldr    x17, [x16, #4008]
 5ac:    913ea210     add    x16, x16, #0xfa8
 5b0:    d61f0220     br    x17
 5b4:    d503201f     nop
 5b8:    d503201f     nop
 5bc:    d503201f     nop

00000000000005c0 <__libc_start_main@plt>:
 5c0:    f00000f0     adrp    x16, 1f000 <__FRAME_END__+0x1e7f4>
 5c4:    f947da11     ldr    x17, [x16, #4016]
 5c8:    913ec210     add    x16, x16, #0xfb0
 5cc:    d61f0220     br    x17

00000000000005d0 <__cxa_finalize@plt>:
 5d0:    f00000f0     adrp    x16, 1f000 <__FRAME_END__+0x1e7f4>
 5d4:    f947de11     ldr    x17, [x16, #4024]
 5d8:    913ee210     add    x16, x16, #0xfb8
 5dc:    d61f0220     br    x17

00000000000005e0 <__gmon_start__@plt>:
 5e0:    f00000f0     adrp    x16, 1f000 <__FRAME_END__+0x1e7f4>
 5e4:    f947e211     ldr    x17, [x16, #4032]
 5e8:    913f0210     add    x16, x16, #0xfc0
 5ec:    d61f0220     br    x17

00000000000005f0 <abort@plt>:
 5f0:    f00000f0     adrp    x16, 1f000 <__FRAME_END__+0x1e7f4>
 5f4:    f947e611     ldr    x17, [x16, #4040]
 5f8:    913f2210     add    x16, x16, #0xfc8
 5fc:    d61f0220     br    x17

Disassembly of section .text:

0000000000000600 <_start>:
 600:    d503201f     nop
 604:    d280001d     mov    x29, #0x0                       // #0
 608:    d280001e     mov    x30, #0x0                       // #0
 60c:    aa0003e5     mov    x5, x0
 610:    f94003e1     ldr    x1, [sp]
 614:    910023e2     add    x2, sp, #0x8
 618:    910003e6     mov    x6, sp
 61c:    f00000e0     adrp    x0, 1f000 <__FRAME_END__+0x1e7f4>
 620:    f947f800     ldr    x0, [x0, #4080]
 624:    d2800003     mov    x3, #0x0                       // #0
 628:    d2800004     mov    x4, #0x0                       // #0
 62c:    97ffffe5     bl    5c0 <__libc_start_main@plt>
 630:    97fffff0     bl    5f0 <abort@plt>

0000000000000634 <call_weak_fn>:
 634:    f00000e0     adrp    x0, 1f000 <__FRAME_END__+0x1e7f4>
 638:    f947f400     ldr    x0, [x0, #4072]
 63c:    b4000040     cbz    x0, 644 <call_weak_fn+0x10>
 640:    17ffffe8     b    5e0 <__gmon_start__@plt>
 644:    d65f03c0     ret
 648:    d503201f     nop
 64c:    d503201f     nop

0000000000000650 <deregister_tm_clones>:
 650:    90000100     adrp    x0, 20000 <__data_start>
 654:    91004000     add    x0, x0, #0x10
 658:    90000101     adrp    x1, 20000 <__data_start>
 65c:    91004021     add    x1, x1, #0x10
 660:    eb00003f     cmp    x1, x0
 664:    540000c0     b.eq    67c <deregister_tm_clones+0x2c>  // b.none
 668:    f00000e1     adrp    x1, 1f000 <__FRAME_END__+0x1e7f4>
 66c:    f947ec21     ldr    x1, [x1, #4056]
 670:    b4000061     cbz    x1, 67c <deregister_tm_clones+0x2c>
 674:    aa0103f0     mov    x16, x1
 678:    d61f0200     br    x16
 67c:    d65f03c0     ret

0000000000000680 <register_tm_clones>:
 680:    90000100     adrp    x0, 20000 <__data_start>
 684:    91004000     add    x0, x0, #0x10
 688:    90000101     adrp    x1, 20000 <__data_start>
 68c:    91004021     add    x1, x1, #0x10
 690:    cb000021     sub    x1, x1, x0
 694:    d37ffc22     lsr    x2, x1, #63
 698:    8b810c41     add    x1, x2, x1, asr #3
 69c:    9341fc21     asr    x1, x1, #1
 6a0:    b40000c1     cbz    x1, 6b8 <register_tm_clones+0x38>
 6a4:    f00000e2     adrp    x2, 1f000 <__FRAME_END__+0x1e7f4>
 6a8:    f947fc42     ldr    x2, [x2, #4088]
 6ac:    b4000062     cbz    x2, 6b8 <register_tm_clones+0x38>
 6b0:    aa0203f0     mov    x16, x2
 6b4:    d61f0200     br    x16
 6b8:    d65f03c0     ret
 6bc:    d503201f     nop

00000000000006c0 <__do_global_dtors_aux>:
 6c0:    a9be7bfd     stp    x29, x30, [sp, #-32]!
 6c4:    910003fd     mov    x29, sp
 6c8:    f9000bf3     str    x19, [sp, #16]
 6cc:    90000113     adrp    x19, 20000 <__data_start>
 6d0:    39404260     ldrb    w0, [x19, #16]
 6d4:    37000140     tbnz    w0, #0, 6fc <__do_global_dtors_aux+0x3c>
 6d8:    f00000e0     adrp    x0, 1f000 <__FRAME_END__+0x1e7f4>
 6dc:    f947f000     ldr    x0, [x0, #4064]
 6e0:    b4000080     cbz    x0, 6f0 <__do_global_dtors_aux+0x30>
 6e4:    90000100     adrp    x0, 20000 <__data_start>
 6e8:    f9400400     ldr    x0, [x0, #8]
 6ec:    97ffffb9     bl    5d0 <__cxa_finalize@plt>
 6f0:    97ffffd8     bl    650 <deregister_tm_clones>
 6f4:    52800020     mov    w0, #0x1                       // #1
 6f8:    39004260     strb    w0, [x19, #16]
 6fc:    f9400bf3     ldr    x19, [sp, #16]
 700:    a8c27bfd     ldp    x29, x30, [sp], #32
 704:    d65f03c0     ret
 708:    d503201f     nop
 70c:    d503201f     nop

0000000000000710 <frame_dummy>:
 710:    17ffffdc     b    680 <register_tm_clones>

0000000000000714 <main>:
 714:    52800540     mov    w0, #0x2a                      // #42
 718:    d65f03c0     ret

Disassembly of section .fini:

000000000000071c <_fini>:
 71c:    d503201f     nop
 720:    a9bf7bfd     stp    x29, x30, [sp, #-16]!
 724:    910003fd     mov    x29, sp
 728:    a8c17bfd     ldp    x29, x30, [sp], #16
 72c:    d65f03c0     ret

出力結果

コンパイルによってできた実行ファイルを実行
user@6a6a8f30e625:/9cc$ ./test1

コマンド終了直後に$?をechoで表示することで、そのコマンドの終了結果を確認できる。正しく42が返されている。

user@6a6a8f30e625:/9cc$ echo $?
42

今回のまとめ

  • Dockerのオプションの意味を深く理解できる機会となった。
  • ccコマンドにより、C言語のソースプログラムがコンピュータが理解できる「機械語命令(アセンブリ言語)」に変換される。