編纂者:hsjoihs
CFLAGS=-std=c17 -Wall -s
3: 3.c
42: 42.c
clean:
rm 3
rm 42
.PHONY: clean
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ wc -c 3
13296 3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make 3
cc -std=c17 -Wall -s -Os 3.c -o 3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make 42
cc -std=c17 -Wall -s -Os 42.c -o 42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ wc -c 3
14328 3
; tiny.asm
BITS 32
GLOBAL _start
SECTION .text
_start:
mov eax, 1
mov ebx, 42
int 0x80
tiny42: tiny42.asm
nasm -f elf tiny42.asm
gcc -Wall -s -nostdlib tiny42.o -o tiny42
nasm -f elf tiny42.asm
gcc -Wall -s -nostdlib tiny42.o -o tiny42
/usr/bin/ld: i386 architecture of input file `tiny42.o' is incompatible with i386:x86-64 output
collect2: error: ld returned 1 exit status
make: *** [Makefile:12: tiny42] Error 1
.globl _start
_start:
movl $1, %eax
movl $42, %ebx
int $0x80
tiny42: tiny42.s
gcc -Wall -s -nostdlib tiny42.s -o tiny42
nasm -felf64 hello.asm && ld hello.o && ./a.out
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make tiny42
nasm -felf64 tiny42.asm
ld tiny42.o -o tiny42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ ./tiny42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ echo $?
42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ wc -c tiny42
4672 tiny42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ nasm --version
NASM version 2.14.02
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ sudo apt-get -y install nasm
Reading package lists... Done
Building dependency tree
Reading state information... Done
nasm is already the newest version (2.14.02-1).
The following package was automatically installed and is no longer required:
libfwupdplugin1
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 44 not upgraded.
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ nasm --version
NASM version 2.15.05 compiled on Oct 3 2022
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make tiny42
nasm -felf64 --reproducible tiny42.asm
ld tiny42.o -o tiny42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make tiny3
nasm -felf64 --reproducible tiny3.asm
ld tiny3.o -o tiny3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ ./tiny42; echo $?
42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ ./tiny3; echo $?
3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ cmp -l ./tiny42 ./tiny3
4103 52 3
4185 21 20
4209 14 13
4233 30 27
4257 37 36
4286 64 63
4287 62 56
4288 56 141
4289 141 163
4290 163 155
4291 155 0
4292 0 137
4294 137 142
4295 142 163
4297 163 137
4298 137 163
4299 163 164
4300 164 141
4301 141 162
4302 162 164
4303 164 0
4304 0 137
4305 137 145
4306 145 144
4307 144 141
4308 141 164
4309 164 141
4310 141 0
4311 0 137
4312 137 145
4313 145 156
4314 156 144
4315 144 0
4317 0 56
4318 56 163
4319 163 171
4320 171 155
4321 155 164
4322 164 141
4323 141 142
4324 142 0
4325 0 56
4326 56 163
4327 163 164
4328 164 162
4329 162 164
4330 164 141
4331 141 142
4332 142 0
4333 0 56
4334 56 163
4335 163 150
4336 150 163
4337 163 164
4338 164 162
4339 162 164
4340 164 141
4341 141 142
4342 142 0
4343 0 56
4344 56 164
4345 164 145
4346 145 170
4347 170 164
4348 164 0
4577 44 43
4633 334 333
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make tiny42
nasm -felf64 --reproducible tiny42.asm
ld tiny42.o -o tiny42
strip tiny42
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ make tiny3
nasm -felf64 --reproducible tiny3.asm
ld tiny3.o -o tiny3
strip tiny3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ ./tiny42; echo $?; ./tiny3; echo $?
42
3
hsjoihs@LAPTOP-BKHPSENK:~/c_to_elf_compiler/experiment$ cmp -l ./tiny42 ./tiny3
4103 52 3
fn main() -> std::io::Result<()> {
let input = std::env::args().nth(1).expect("入力が与えられていません");
let input: u8 = input.parse().expect("入力をパースできません");
let tiny_3 = include_bytes!("../experiment/tiny3");
let tiny_42 = include_bytes!("../experiment/tiny42");
assert_eq!(tiny_3.len(), tiny_42.len());
let file = std::fs::File::create("a.out")?;
{
use std::io::Write;
let mut writer = std::io::BufWriter::new(file);
for (index, byte) in tiny_3.iter().enumerate() {
if *byte == tiny_42[index] {
writer.write_all(&[*byte])?;
} else if *byte == 3 && tiny_42[index] == 42 {
writer.write_all(&[input])?;
} else {
panic!("`../experiment/tiny3` と `../experiment/tiny42` の間に非自明な差分が見つかったので、なにを出力すべきか分かりません")
}
}
}
Ok(())
}
説明の都合上、一気に*、/、()を実装しているような話の流れになっていますが、実際には一気に実装することは避ける方がよいでしょう。 元々、加減算ができる機能があったわけですから、まずはその機能を壊さずに、抽象構文木とそれを使ったコードジェネレータを導入するようにしてみてください。 そのときには新たな機能を足すわけではないので、新しいテストは必要ありません。その後に、*、/、()を、テスト込みで実装していってください。
☑ 即値をプッシュ ☒ ediをプッシュ ☑ ediへとポップ ☒ eaxへとポップ ☒ ediにeaxを足し合わせる ☒ ediからeaxを減じる
fn 即値をプッシュ(n: u8) -> [u8; 2] {
[0x6a, n]
}
fn ediをプッシュ() -> [u8; 1] {
[0x57]
}
fn ediへとポップ() -> [u8; 1] {
[0x5f]
}
fn eaxへとポップ() -> [u8; 1] {
[0x58]
}
fn ediにeaxを足し合わせる() -> [u8; 2] {
[0x01, 0xc7]
}
fn ediからeaxを減じる() -> [u8; 2] {
[0x29, 0xc7]
}
fn exprを評価してediレジスタへ(writer: &mut impl Write, expr: &Expr) {
match expr {
Expr::BinaryExpr {
op: BinaryOp::Add,
op_pos: _,
左辺,
右辺,
} => {
exprを評価してediレジスタへ(writer, 左辺);
writer.write_all(&ediをプッシュ()).unwrap();
exprを評価してediレジスタへ(writer, 右辺);
writer.write_all(&ediをプッシュ()).unwrap();
writer.write_all(&eaxへとポップ()).unwrap();
writer.write_all(&ediへとポップ()).unwrap();
writer.write_all(&ediにeaxを足し合わせる()).unwrap();
}
Expr::BinaryExpr {
op: BinaryOp::Sub,
op_pos: _,
左辺,
右辺,
} => {
exprを評価してediレジスタへ(writer, 左辺);
writer.write_all(&ediをプッシュ()).unwrap();
exprを評価してediレジスタへ(writer, 右辺);
writer.write_all(&ediをプッシュ()).unwrap();
writer.write_all(&eaxへとポップ()).unwrap();
writer.write_all(&ediへとポップ()).unwrap();
writer.write_all(&ediからeaxを減じる()).unwrap();
}
Expr::Primary { val, pos: _ } => {
writer.write_all(&ediに代入(*val)).unwrap();
}
}
}
fn parse_and_codegen(
mut writer: &mut impl Write,
tokens: &[Token],
input: &str,
) -> Result<(), AppError> {
let expr = parse(tokens, input)?;
let tiny = include_bytes!("../experiment/tiny");
writer.write_all(&tiny[0..0x78]).unwrap();
writer.write_all(&[0xb8, 0x3c, 0x00, 0x00, 0x00]).unwrap();
exprを評価してediレジスタへ(&mut writer, &expr);
writer.write_all(&[0x0f, 0x05]).unwrap();
Ok(())
}
[FAIL] 1+2 => 3 expected, but got 139
fn ediをeax倍にする() -> [u8; 3] {
[0x0f, 0xaf, 0xf8]
}
特にパースやコード生成において重要なポイントではないのですが、トリッキーな仕様のidiv命令が上のコードでは使われているので、それについて説明しておきましょう。 idivは符号あり除算を行う命令です。x86-64のidivが素直な仕様になっていれば、上のコードでは本来idiv rax, rdiのように書きたかったところですが、そのような2つのレジスタをとる除算命令はx86-64には存在しません。その代わりに、idivは暗黙のうちにRDXとRAXを取って、それを合わせたものを128ビット整数とみなして、それを引数のレジスタの64ビットの値で割り、商をRAXに、余りをRDXにセットする、という仕様になっています。cqo命令を使うと、RAXに入っている64ビットの値を128ビットに伸ばしてRDXとRAXにセットすることができるので、上記のコードではidivを呼ぶ前にcqoを呼んでいます。
fn eaxの符号ビットをedxへ拡張() -> [u8; 1] {
[0x99]
}
fn edx_eaxをediで割る_商はeaxに_余りはedxに() -> [u8; 2] {
[0xf7, 0xff]
}
fn eaxをプッシュ() -> [u8; 1] {
[0x50]
}
If you already have a workflow to publish your site, you can skip this step. GitHub Pages does not associate a specific workflow to the GitHub Pages settings. However, the GitHub Pages settings will link to the workflow run that most recently deployed your site.
The general flow of a workflow is to: 1. Trigger whenever there is a push to the default branch of the repository or whenever the workflow is run manually from the Actions tab. 2. Use the actions/checkout action to check out the repository contents. 3. If required by your site, build any static site files. 4. Use the actions/upload-pages-artifact action to upload the static files as an artifact. 5. If the workflow was triggered by a push to the default branch, use the actions/deploy-pages action to deploy the artifact. This step is skipped if the workflow was triggered by a pull request.
Error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable at Function.<anonymous> (/home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/oidc-utils.js:71:1) at Generator.next (<anonymous>) at /home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/oidc-utils.js:8:1 at new Promise (<anonymous>) at __webpack_modules__.8041.__awaiter (/home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/oidc-utils.js:4:1) at Function.getIDToken (/home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/oidc-utils.js:57:1) at Object.<anonymous> (/home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/core.js:315:1) at Generator.next (<anonymous>) at /home/runner/work/_actions/actions/deploy-pages/v1/webpack:/deploy-pages/node_modules/@actions/core/lib/core.js:27:1 at new Promise (<anonymous>) Error: Ensure GITHUB_TOKEN has permission "idToken: write".
The workflow is not valid. .github/workflows/docs.yaml (Line: 35, Col: 14): Unexpected value '' .github/workflows/docs.yaml (Line: 36, Col: 9): Unexpected value 'github_token'
For newbies of GitHub Actions: Note that the GITHUB_TOKEN is NOT a personal access token. A GitHub Actions runner automatically creates a GITHUB_TOKEN secret to authenticate in your workflow. So, you can start to deploy immediately without any configuration.
program = stmt* stmt = expr ";" expr = assign assign = equality ("=" assign)? equality = relational ("==" relational | "!=" relational)* relational = add ("<" add | "<=" add | ">" add | ">=" add)* add = mul ("+" mul | "-" mul)* mul = unary ("*" unary | "/" unary)* unary = ("+" | "-")? primary primary = num | ident | "(" expr ")"
Expr::BinaryExpr {
op: BinaryOp::AndThen,
op_pos: _,
左辺,
右辺,
} => {
exprを評価してediレジスタへ(writer, 左辺); // 左辺は push せずに捨てる
exprを評価してediレジスタへ(writer, 右辺);
}
fn rdiから即値を引く(n: u8) -> [u8; 4] {
[0x48, 0x83, 0xef, n]
}
- a = b = 7; a
+ a = b = 7; return a;
__start() { __throw main(); }
main() { ここにテストケースを貼る }
one() { return 1; } two() { return one() + 1; } main() { return one() + two() + __builtin_three(); }
xやyといったローカル変数が存在するものとしてコンパイルして、関数のプロローグの中で、レジスタの値をそのローカル変数のためのスタック上の領域に書き出してください。
thread 'main' panicked at '関数 fib が見つかりません', src/codegen.rs:579:36
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[FAIL] add(x, y, z) { return x + y + z; } main() { return add(1, 2, __builtin_three()); } => 6 expected, but got 139
[FAIL] id(x) { return x; } main() { return id(6); } => 6 expected, but got 139
[FAIL] three(x) { return 3; } main() { return three(6); } => 3 expected, but got 139
このステップが終わるとフィボナッチ数列を再帰で計算しつつ表示したりできるようになるのでグッと面白くなるはずです。
check 0 "if_non0(n) { __builtin_putchar(n ? n + 48 : 32); return 0; } print(n) { if_non0(n / 100); if_non0((n / 10) % 10); __builtin_putchar((n % 10) + 48); return 0; } main() { a = 0; b = 1; for(;a<255;) { print(a); __builtin_putchar(44); c = a+b; a = b; b = c; } return 0; }" " 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,144,233,"
check 0 "if_non0(n) { if (n) { __builtin_putchar(n + 48); } else { __builtin_putchar(32); } return 0; } print(n) { for(h=0;n>=100;h=h+1){n=n-100;} if_non0(h); for(t=0;n>=10;t=t+1){n=n-10;} if_non0(t); __builtin_putchar(n + 48); return 0; } main() { a = 2; b = 1; for(;a<127;) { print(a); __builtin_putchar(44); c = a+b; a = b; b = c; } return 0; }" " 2, 1, 3, 4, 7, 11, 18, 29, 47, 76,123,"
thread 'main' panicked at 'while 文の中でジャンプするためのバッファの長さが i8 に収まりません: TryFromIntError(())', src/codegen.rs:326:65
thread 'main' panicked at 'while 文の中でジャンプするためのバッファの長さが i8 に収まりません。バッファの長さは 155、中身は 0x[55 5f 48 83 ef 04 48 8b 3f 57 bf 7f 00 00 00 57 5f 58 39 f8 0f 9c c0 0f b6 f8 83 ff 00 74 7e 48 83 ec 08 55 5f 48 83 ef 04 48 8b 3f 57 5f b8 0c 01 40 00 ff d0 89 c7 48 83 c4 08 48 83 ec 08 bf 2c 00 00 00 57 5f b8 87 00 40 00 ff d0 89 c7 48 83 c4 08 55 5f 48 83 ef 0c 57 55 5f 48 83 ef 04 48 8b 3f 57 55 5f 48 83 ef 08 48 8b 3f 57 58 5f 01 c7 58 89 38 55 5f 48 83 ef 04 57 55 5f 48 83 ef 08 48 8b 3f 58 89 38 55 5f 48 83 ef 08 57 55 5f 48 83 ef 0c 48 8b 3f 58 89 38] です', src/codegen.rs:327:21
0: 55 push rbp
1: 5f pop rdi
2: 48 83 ef 04 sub rdi,0x4
6: 48 8b 3f mov rdi,QWORD PTR [rdi]
9: 57 push rdi
a: bf 7f 00 00 00 mov edi,0x7f
f: 57 push rdi
10: 5f pop rdi
11: 58 pop rax
12: 39 f8 cmp eax,edi
14: 0f 9c c0 setl al
17: 0f b6 f8 movzx edi,al
1a: 83 ff 00 cmp edi,0x0
1d: 74 7e je 0x9d
1f: 48 83 ec 08 sub rsp,0x8
23: 55 push rbp
24: 5f pop rdi
25: 48 83 ef 04 sub rdi,0x4
29: 48 8b 3f mov rdi,QWORD PTR [rdi]
2c: 57 push rdi
2d: 5f pop rdi
2e: b8 0c 01 40 00 mov eax,0x40010c
33: ff d0 call rax
35: 89 c7 mov edi,eax
37: 48 83 c4 08 add rsp,0x8
3b: 48 83 ec 08 sub rsp,0x8
3f: bf 2c 00 00 00 mov edi,0x2c
44: 57 push rdi
45: 5f pop rdi
46: b8 87 00 40 00 mov eax,0x400087
4b: ff d0 call rax
4d: 89 c7 mov edi,eax
4f: 48 83 c4 08 add rsp,0x8
53: 55 push rbp
54: 5f pop rdi
55: 48 83 ef 0c sub rdi,0xc
59: 57 push rdi
5a: 55 push rbp
5b: 5f pop rdi
5c: 48 83 ef 04 sub rdi,0x4
60: 48 8b 3f mov rdi,QWORD PTR [rdi]
63: 57 push rdi
64: 55 push rbp
65: 5f pop rdi
66: 48 83 ef 08 sub rdi,0x8
6a: 48 8b 3f mov rdi,QWORD PTR [rdi]
6d: 57 push rdi
6e: 58 pop rax
6f: 5f pop rdi
70: 01 c7 add edi,eax
72: 58 pop rax
73: 89 38 mov DWORD PTR [rax],edi
75: 55 push rbp
76: 5f pop rdi
77: 48 83 ef 04 sub rdi,0x4
7b: 57 push rdi
7c: 55 push rbp
7d: 5f pop rdi
7e: 48 83 ef 08 sub rdi,0x8
82: 48 8b 3f mov rdi,QWORD PTR [rdi]
85: 58 pop rax
86: 89 38 mov DWORD PTR [rax],edi
88: 55 push rbp
89: 5f pop rdi
8a: 48 83 ef 08 sub rdi,0x8
8e: 57 push rdi
8f: 55 push rbp
90: 5f pop rdi
91: 48 83 ef 0c sub rdi,0xc
95: 48 8b 3f mov rdi,QWORD PTR [rdi]
98: 58 pop rax
99: 89 38 mov DWORD PTR [rax],edi
if_non0(n)
{
if (n) {
__builtin_putchar(n + 48);
}
else {
__builtin_putchar(32);
}
return 0;
}
print(n)
{
for (h = 0; n >= 100; h = h + 1) {
n = n - 100;
}
if_non0(h);
for (t = 0; n >= 10; t = t + 1) {
n = n - 10;
}
if_non0(t);
__builtin_putchar(n + 48);
return 0;
}
printcomma(n)
{
print(n);
__builtin_putchar(44);
return n;
}
main()
{
a = 2;
b = 1;
while (a < 127) {
c = printcomma(a) + b;
a = b;
b = c;
}
return 0;
}
thread 'main' panicked at 'while 文の中でジャンプするためのバッファの長さが i8 に収まりません。バッファの長さは 134、中身は 0x[55 5f 48 83 ef 04 48 8b 3f 57 bf 14 00 00 00 57 bf 14 00 00 00 57 58 5f 0f af f8 57 5f 58 39 f8 0f 9c c0 0f b6 f8 83 ff 00 74 5d 55 5f 48 83 ef 0c 57 48 83 ec 0c 55 5f 48 83 ef 04 48 8b 3f 57 5f b8 57 02 40 00 ff d0 89 c7 48 83 c4 0c 57 55 5f 48 83 ef 08 48 8b 3f 57 58 5f 01 c7 58 89 38 55 5f 48 83 ef 04 57 55 5f 48 83 ef 08 48 8b 3f 58 89 38 55 5f 48 83 ef 08 57 55 5f 48 83 ef 0c 48 8b 3f 58 89 38] です', src/codegen.rs:327:21
print(n) {
for (t = 0; n >= 10; t = t + 1) {
n = n - 10;
}
if (t) {
__builtin_putchar(t + 48);
}
else {
__builtin_putchar(32);
}
__builtin_putchar(n + 48);
return 0;
}
printcomma(n) {
print(n);
__builtin_putchar(44);
return n;
}
main() {
a = 0; b = 1;
while (a < 100) { c = printcomma(a) + b; a = b; b = c; }
return 0;
}
'else でジャンプするためのバッファの長さが i8 に収まりません。バッファの長さは 130、中身は 0x[55 5f 48 83 ef 04 48 8b 3f 57 bf 01 00 00 00 57 5f 58 39 f8 0f 94 c0 0f b6 f8 83 ff 00 74 0b bf 01 00 00 00 89 f8 c9 c3 eb 58 48 83 ec 08 55 5f 48 83 ef 04 48 8b 3f 57 bf 01 00 00 00 57 58 5f 29 c7 57 5f b8 a9 00 40 00 ff d0 89 c7 48 83 c4 08 57 48 83 ec 0c 55 5f 48 83 ef 04 48 8b 3f 57 bf 02 00 00 00 57 58 5f 29 c7 57 5f b8 a9 00 40 00 ff d0 89 c7 48 83 c4 0c 57 58 5f 01 c7 89 f8 c9 c3] です', src/codegen.rs:303:41
if_non0(n) { if (n) { __builtin_putchar(n + 48); } else { __builtin_putchar(32); } return 0; } print(n) { for(h=0;n>=100;h=h+1){n=n-100;} if_non0(h); for(t=0;n>=10;t=t+1){n=n-10;} if_non0(t); __builtin_putchar(n + 48); return 0; } printcomma(n) { print(n); __builtin_putchar(44); return n; } fib(n) { if(n == 0){ return 0; } else if(n == 1) { return 1; } return fib(n-1) + fib(n-2); } main() { for(n=0;n<17;n=n+1) {printcomma(fib(n));} return 0; }
pub struct FunctionDefinition {
pub func_name: String,
pub params: Vec<(Type, ParameterIdentifier)>,
pub pos: usize,
pub content: FunctionContent,
pub return_type: Type
}
pub enum Expr {
BinaryExpr {
op: BinaryOp,
op_pos: usize,
左辺: Box<Expr>,
右辺: Box<Expr>,
typ: Type,
},
Numeric {
val: u8,
pos: usize,
typ: Type,
},
Identifier {
ident: String,
pos: usize,
typ: Type,
},
Call {
ident: String,
pos: usize,
args: Vec<Expr>,
typ: Type,
},
UnaryExpr {
op: UnaryOp,
op_pos: usize,
expr: Box<Expr>,
typ: Type,
},
}
この段階ではまだ連続してメモリをアロケートする方法がないので(我々のコンパイラにはまだ配列がない)、テストを書くのはちょっと大変です。ここは単に外部のコンパイラの助けを借りて、そちらのほうでmallocすることにして、自分のコンパイラの出力ではそのヘルパー関数を使ってテストを書くようにしてみてください。
#include <unistd.h>
int brk(void* end_data_segment);
void *sbrk(intptr_t increment);
int alloc4(int **pp, int a, int b, int c, int d) {
int *p = sbrk(0);
brk(p + 4);
p[0] = a;
p[1] = b;
p[2] = c;
p[3] = d;
*pp = p;
return 0;
}
#include <unistd.h>
#include <stdio.h>
int brk(void* end_data_segment);
void *sbrk(intptr_t increment);
int alloc4(int **pp, int a, int b, int c, int d) {
int *p = sbrk(0);
brk(p + 4);
p[0] = a;
p[1] = b;
p[2] = c;
p[3] = d;
*pp = p;
return 0;
}
int main() {
int *p;
alloc4(&p, 1, 2, 4, 8);
int *q;
q = p + 2;
printf("%d, ", *q); // 4
q = p + 3;
printf("%d\n", *q); // 8
return 0;
}
However, the actual Linux system call returns the new program break on success. On failure, the system call returns the current break.
[FAIL] int main() { int *p; p = __builtin_alloc4(1, 2, 4, 8); return *p + *(p+1) + *(p+2) + *(p+3); } => 15 expected, but got 1
hsjoihs@LAPTOP-BKHPSENK:~/c-compilers/c_to_elf_compiler$ gdb failing.out
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from failing.out...
(No debugging symbols found in failing.out)
(gdb) run
Starting program: /home/hsjoihs/c-compilers/c_to_elf_compiler/failing.out
[Inferior 1 (process 10911) exited with code 01]
(gdb) start
No symbol table is loaded. Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Temporary breakpoint 1 (-qualified main) pending.
Starting program: /home/hsjoihs/c-compilers/c_to_elf_compiler/failing.out
[Inferior 1 (process 10945) exited with code 01]
(gdb) file
No executable file now.
No symbol file now.
(gdb)
hsjoihs@LAPTOP-BKHPSENK:~/c-compilers/c_to_elf_compiler$ objdump -d failing.out
failing.out: file format elf64-x86-64
えーとこれが __builtin_putchar よね。あれ?__builtin_three はどこ行った?
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 08 sub rsp,0x8
8: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
b: b8 01 00 00 00 mov eax,0x1
10: bf 01 00 00 00 mov edi,0x1
15: 48 8d 75 f8 lea rsi,[rbp-0x8]
19: ba 01 00 00 00 mov edx,0x1
1e: 0f 05 syscall
20: c9 leave
21: c3 ret
次にこれが __builtin_alloc4 だけど……あっプロローグでローカル変数の分の引き算をしていない!!これだ
22: 55 push rbp
23: 48 89 e5 mov rbp,rsp
26: 48 83 ec 08 sub rsp,0x8
2a: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
2d: 89 75 f0 mov DWORD PTR [rbp-0x10],esi
30: 89 55 e8 mov DWORD PTR [rbp-0x18],edx
33: 89 4d e0 mov DWORD PTR [rbp-0x20],ecx
36: b8 0c 00 00 00 mov eax,0xc
3b: bf 00 00 00 00 mov edi,0x0
40: 0f 05 syscall
42: 50 push rax
43: 5f pop rdi
44: b8 10 00 00 00 mov eax,0x10
49: 48 01 c7 add rdi,rax
4c: b8 0c 00 00 00 mov eax,0xc
51: 0f 05 syscall
53: 48 83 e8 04 sub rax,0x4
57: 48 8d 7d e0 lea rdi,[rbp-0x20]
5b: 48 8b 3f mov rdi,QWORD PTR [rdi]
5e: 48 89 38 mov QWORD PTR [rax],rdi
61: 48 83 e8 04 sub rax,0x4
65: 48 8d 7d e8 lea rdi,[rbp-0x18]
69: 48 8b 3f mov rdi,QWORD PTR [rdi]
6c: 48 89 38 mov QWORD PTR [rax],rdi
6f: 48 83 e8 04 sub rax,0x4
73: 48 8d 7d f0 lea rdi,[rbp-0x10]
77: 48 8b 3f mov rdi,QWORD PTR [rdi]
7a: 48 89 38 mov QWORD PTR [rax],rdi
7d: 48 83 e8 04 sub rax,0x4
81: 48 8d 7d f8 lea rdi,[rbp-0x8]
85: 48 8b 3f mov rdi,QWORD PTR [rdi]
88: 48 89 38 mov QWORD PTR [rax],rdi
8b: c9 leave
8c: c3 ret
ああ普通に __builtin_three あるね。さっきはコピペ範囲をミスっただけか
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 00 sub rsp,0x0
8: b8 03 00 00 00 mov eax,0x3
d: c9 leave
e: c3 ret
次にこれが __builtin_putchar で、
f: 55 push rbp
10: 48 89 e5 mov rbp,rsp
13: 48 83 ec 08 sub rsp,0x8
17: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
1a: b8 01 00 00 00 mov eax,0x1
1f: bf 01 00 00 00 mov edi,0x1
24: 48 8d 75 f8 lea rsi,[rbp-0x8]
28: ba 01 00 00 00 mov edx,0x1
2d: 0f 05 syscall
2f: c9 leave
30: c3 ret
お次のこれが __builtin_alloc4 のはず。今度はちゃんと 0x20 を引いてるんだけどなぁ
31: 55 push rbp
32: 48 89 e5 mov rbp,rsp
35: 48 83 ec 20 sub rsp,0x20
39: 89 7d f8 mov DWORD PTR [rbp-0x8],edi
3c: 89 75 f0 mov DWORD PTR [rbp-0x10],esi
3f: 89 55 e8 mov DWORD PTR [rbp-0x18],edx
42: 89 4d e0 mov DWORD PTR [rbp-0x20],ecx
45: b8 0c 00 00 00 mov eax,0xc
4a: bf 00 00 00 00 mov edi,0x0
4f: 0f 05 syscall
51: 50 push rax
52: 5f pop rdi
53: b8 10 00 00 00 mov eax,0x10
58: 48 01 c7 add rdi,rax
5b: b8 0c 00 00 00 mov eax,0xc
60: 0f 05 syscall
62: 48 83 e8 04 sub rax,0x4
66: 48 8d 7d e0 lea rdi,[rbp-0x20]
6a: 48 8b 3f mov rdi,QWORD PTR [rdi]
6d: 48 89 38 mov QWORD PTR [rax],rdi
70: 48 83 e8 04 sub rax,0x4
74: 48 8d 7d e8 lea rdi,[rbp-0x18]
78: 48 8b 3f mov rdi,QWORD PTR [rdi]
7b: 48 89 38 mov QWORD PTR [rax],rdi
7e: 48 83 e8 04 sub rax,0x4
82: 48 8d 7d f0 lea rdi,[rbp-0x10]
86: 48 8b 3f mov rdi,QWORD PTR [rdi]
89: 48 89 38 mov QWORD PTR [rax],rdi
8c: 48 83 e8 04 sub rax,0x4
90: 48 8d 7d f8 lea rdi,[rbp-0x8]
94: 48 8b 3f mov rdi,QWORD PTR [rdi]
97: 48 89 38 mov QWORD PTR [rax],rdi
9a: c9 leave
9b: c3 ret
で、こいつが今回試されるべき int main() { int *p; p = __builtin_alloc4(1, 2, 4, 8); return *p + *(p+1) + *(p+2) + *(p+3); }
9c: 55 push rbp
9d: 48 89 e5 mov rbp,rsp
a0: 48 83 ec 08 sub rsp,0x8
a4: 55 push rbp
a5: 5f pop rdi
a6: 48 83 ef 08 sub rdi,0x8
aa: 57 push rdi
ab: 48 83 ec 10 sub rsp,0x10
af: bf 08 00 00 00 mov edi,0x8
b4: 57 push rdi
b5: bf 04 00 00 00 mov edi,0x4
ba: 57 push rdi
bb: bf 02 00 00 00 mov edi,0x2
c0: 57 push rdi
c1: bf 01 00 00 00 mov edi,0x1
c6: 57 push rdi
c7: 5f pop rdi
c8: 5e pop rsi
c9: 5a pop rdx
ca: 59 pop rcx
cb: b8 a9 00 40 00 mov eax,0x4000a9
d0: ff d0 call rax
d2: 89 c7 mov edi,eax
d4: 48 83 c4 10 add rsp,0x10
d8: 58 pop rax
d9: 48 89 38 mov QWORD PTR [rax],rdi
dc: 55 push rbp
dd: 5f pop rdi
de: 48 83 ef 08 sub rdi,0x8
e2: 48 8b 3f mov rdi,QWORD PTR [rdi]
e5: 48 8b 3f mov rdi,QWORD PTR [rdi]
e8: 57 push rdi
e9: 55 push rbp
ea: 5f pop rdi
eb: 48 83 ef 08 sub rdi,0x8
ef: 48 8b 3f mov rdi,QWORD PTR [rdi]
f2: 57 push rdi
f3: bf 04 00 00 00 mov edi,0x4
f8: 57 push rdi
f9: bf 01 00 00 00 mov edi,0x1
fe: 57 push rdi
ff: 58 pop rax
100: 5f pop rdi
101: 48 0f af f8 imul rdi,rax
105: 57 push rdi
106: 58 pop rax
107: 5f pop rdi
108: 48 01 c7 add rdi,rax
10b: 48 8b 3f mov rdi,QWORD PTR [rdi]
10e: 57 push rdi
10f: 58 pop rax
110: 5f pop rdi
111: 48 01 c7 add rdi,rax
114: 57 push rdi
115: 55 push rbp
116: 5f pop rdi
117: 48 83 ef 08 sub rdi,0x8
11b: 48 8b 3f mov rdi,QWORD PTR [rdi]
11e: 57 push rdi
11f: bf 04 00 00 00 mov edi,0x4
124: 57 push rdi
125: bf 02 00 00 00 mov edi,0x2
12a: 57 push rdi
12b: 58 pop rax
12c: 5f pop rdi
12d: 48 0f af f8 imul rdi,rax
131: 57 push rdi
132: 58 pop rax
133: 5f pop rdi
134: 48 01 c7 add rdi,rax
137: 48 8b 3f mov rdi,QWORD PTR [rdi]
13a: 57 push rdi
13b: 58 pop rax
13c: 5f pop rdi
13d: 48 01 c7 add rdi,rax
140: 57 push rdi
141: 55 push rbp
142: 5f pop rdi
143: 48 83 ef 08 sub rdi,0x8
147: 48 8b 3f mov rdi,QWORD PTR [rdi]
14a: 57 push rdi
14b: bf 04 00 00 00 mov edi,0x4
150: 57 push rdi
151: bf 03 00 00 00 mov edi,0x3
156: 57 push rdi
157: 58 pop rax
158: 5f pop rdi
159: 48 0f af f8 imul rdi,rax
15d: 57 push rdi
15e: 58 pop rax
15f: 5f pop rdi
160: 48 01 c7 add rdi,rax
163: 48 8b 3f mov rdi,QWORD PTR [rdi]
166: 57 push rdi
167: 58 pop rax
168: 5f pop rdi
169: 48 01 c7 add rdi,rax
16c: 89 f8 mov eax,edi
16e: c9 leave
16f: c3 ret
170: 55 push rbp
171: 48 89 e5 mov rbp,rsp
174: 48 83 ec 00 sub rsp,0x0
178: 48 83 ec 08 sub rsp,0x8
17c: b8 14 01 40 00 mov eax,0x400114
181: ff d0 call rax
183: 89 c7 mov edi,eax
185: 48 83 c4 08 add rsp,0x8
189: b8 3c 00 00 00 mov eax,0x3c
18e: 0f 05 syscall
check 1 "int main() { int *p; p = __builtin_alloc4(1, 2, 4, 8); return *p; }"
check 7 "int main() { int *p; p = __builtin_alloc4(7, 2, 4, 8); return *p; }"
check 11 "int main() { int *p; p = __builtin_alloc4(11, 2, 4, 8); return *p; }"
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 1, 4, 8); return *(p+1); } => 1 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 7, 4, 8); return *(p+1); } => 7 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 11, 4, 8); return *(p+1); } => 11 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 1, 8); return *(p+2); } => 1 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 7, 8); return *(p+2); } => 7 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 11, 8); return *(p+2); } => 11 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 8, 1); return *(p+3); } => 1 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 8, 7); return *(p+3); } => 7 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(2, 4, 8, 11); return *(p+3); } => 11 expected, but got 0
[FAIL] int main() { int *p; p = __builtin_alloc4(3, 3, 3, 3); *(p+3) = 8; *(p+2) = 4; *(p+1) = 2; *p = 1; return *p + *(p+1) + *(p+2) + *(p+3); } => 15 expected, but got 1
match typ.sizeof() {
8 => buf.append(raxが指す位置にrdiを代入()),
4 => buf.append(raxが指す位置にediを代入()),
_ => panic!("size が {} な型への代入はできません", typ.sizeof()),
};
pub fn decay_if_arr(expr: Expr) -> Box_<Expr> {
match expr.typ() {
Type::Arr(t, _) => Box_(Box::new(Expr::DecayedArr {
expr: Box_(Box::new(expr)),
typ: Type::Ptr(t),
})),
_ => Box_(Box::new(expr)),
}
}
pub fn no_decay_even_if_arr(expr: Expr) -> Box_<Expr> {
Box_(Box::new(expr))
}
予定変更! @すーぱーてけらには引き続き 24 と 26 もやってもらいますが、私は 23 と 25 の実装をサボります 理由:グローバル変数は外部リンケージを持つ存在なので、「幕間」のあとにやるのが自然 「幕間」と呼んでいるのは、「今回の縛り特有」でどうしても必要になってくる要素をいくつか導入するフェーズ 幕間 1: プロトタイプ宣言と相互再帰 - 現状の Buf を改造して、「名前付き穴」みたいなものを書けるようにし、.to_vec() で最終的に書き込むときにその「名前付き穴」を解決するようにする 幕間 2: 初めての依存ライブラリ、初めての中間ファイル - cargo add serde して、「名前付き穴」のある Buf を JSON として出力してしまう - その出力ファイルを読み、「名前付き穴」を解決し、ELF として吐く仕組みを作る 幕間 3: Yet Another Linkable Format.json - 複数の出力ファイルを読み、「名前付き穴」を解決し、ELF として吐く仕組みを作る - 分割コンパイルのテストケースを書く ということで、私の step 23 は「グローバル変数が定義できるが、sizeof しか取ることができず、読み書きしようとするとコンパイルエラー」で一旦完成とします。同様に step 25 の文字列リテラルも「sizeof しか取ることができない定数」にします