勉強した範囲
- 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言語のソースプログラムがコンピュータが理解できる「機械語命令(アセンブリ言語)」に変換される。