C言語引数の受け渡し処理をアセンブリ上で観察

## 元となるソース

```demo.c
//#include<stdio.h> /* debug */

// demo.c
int add(int a, int b, int c, int d) {
    return a + b + c + d;
}

int main() {
    int result = add(1, 2, 3, 4);
//	printf("result = %d",result); /* debug */
    return result;
}
```
## コンパイル方法
```
gcc -S -masm=intel -o demo.s demo.c
```
|オプション  |意味  |
|---|---|
|S  |アセンブリ言語へ変換  |
|masm=intel  |初期設定のAT&T記法ではなくintel記法  |
|o  |出力ファイル名を指定  |

※AT&T記法で書かれたアセンブリ言語は個人的に読みづらいのでintel記法を設定しています。

## 生成されたアセンブリのコード
※注釈を付けています
```demo.s
	.file	"demo.c"
	.intel_syntax noprefix
	.text
	.globl	add
	.def	add;	.scl	2;	.type	32;	.endef
	.seh_proc	add
add:
	push	rbp
	.seh_pushreg	rbp
	mov	rbp, rsp
	.seh_setframe	rbp, 0
	.seh_endprologue
	mov	DWORD PTR 16[rbp], ecx ;第1引数 int a
	mov	DWORD PTR 24[rbp], edx ;第2引数 int b
	mov	DWORD PTR 32[rbp], r8d ;第3引数 int c
	mov	DWORD PTR 40[rbp], r9d ;第4引数 int d
	mov	edx, DWORD PTR 16[rbp] ;edx = a;
	mov	eax, DWORD PTR 24[rbp] ;eax = b;
	add	edx, eax               ;edx = edx + eax
	mov	eax, DWORD PTR 32[rbp] ;eax = c
	add	edx, eax               ;edx = edx + eax
	mov	eax, DWORD PTR 40[rbp] ;eax = d
	add	eax, edx               ;eax = eax + edx
	;↑mainでは返り値をeaxから取り出す。明示的にreturnしている訳ではない。
	pop	rbp
	ret
	.seh_endproc
	.globl	main
	.def	main;	.scl	2;	.type	32;	.endef
	.seh_proc	main
main:
	push	rbp
	.seh_pushreg	rbp
	mov	rbp, rsp
	.seh_setframe	rbp, 0
	sub	rsp, 48
	.seh_stackalloc	48
	.seh_endprologue
	call	__main
	mov	r9d, 4 ;引数格納
	mov	r8d, 3 ;引数格納
	mov	edx, 2 ;引数格納
	mov	ecx, 1 ;引数格納
	call	add ;add関数呼び出し
	mov	DWORD PTR -4[rbp], eax
	mov	eax, DWORD PTR -4[rbp]
	add	rsp, 48
	pop	rbp
	ret
	.seh_endproc
	.def	__main;	.scl	2;	.type	32;	.endef
	.ident	"GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0"
```
## コンパイル後のアセンブリ分析
mainからaddへの変数引き渡しはレジスタr9d,r8d,edx,ecxを通じて行われていることが分かります。
```s
	mov	r9d, 4 ;
	mov	r8d, 3 ;
	mov	edx, 2 ;
	mov	ecx, 1 ;
```
add側ではそれぞれのレジスタから値を取りだしスタックへ格納しています。
```s
	mov	DWORD PTR 16[rbp], ecx
	mov	DWORD PTR 24[rbp], edx
	mov	DWORD PTR 32[rbp], r8d
	mov	DWORD PTR 40[rbp], r9d
```

## GDBを用いてスタックの中身を確認
```bash
C:\Users\nanasi\kansuasm>gcc -g -O0 -Wl,--disable-dynamicbase demo.c -o demo_debug.exe
C:\Users\nanasi\kansuasm>gdb demo_debug.exe
```
|オプション  |意味  |
|---|---|
|O0  |最適化レベル0。C言語の順序通りにコンパイル。未使用変数の削除無し  |
|masm=intel  |メモリアドレスをランダムにする機能を停止する  |
|Wl  |つけた方がよい？？？(理解してません) |
|o  |出力ファイル名を指定  |

### GDB上での操作
```
(gdb) set disassembly-flavor intel　初期設定のAT&T記法ではなくintel記法
(gdb) disassemble add　add関数に対応するコードのアドレス及びアセンブリのコードが見られる
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/0c6b2023-ea2c-41f8-8da6-091a009f7052.png)

add関数の処理が終わった後のスタックの中身を確認したいので
0x00000001400016f4 <+36>:    pop    rbp
の位置にブレイクポイントを設置しプログラムを実行。

```gdbコマンド.bash
(gdb) break *0x00000001400016f4　ブレイクポイントを設置
(gdb) run　ブレークポイントまで実行
(gdb) x/dw $rbp+16　値を確認する
(gdb) x/dw $rbp+24
(gdb) x/dw $rbp+32
(gdb) x/dw $rbp+40
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4054974/bc350805-9f2b-4fa9-b778-479122910f7a.png)
するとmain関数から渡した1,2,3,4がきちんと代入されていることが分かります。

## 動作環境
Windows10 Version 10.0.19045.5737
gcc version 14.2.0
GNU gdb (GDB for MinGW-W64 x86_64, built by Brecht Sanders, r3) 16.2

スタックの操作や構造は後日研究して又記事を上げます。
今回は観察なのでまとまりが無いかもしれませんがご容赦下さい。