C/C++ (cdecl) における関数呼び出しの stack 処理

復習 for me.

# 処理を理解するうえで押さえておくべき registers
* EIP: Instruction Pointer、次に実行される address を指す pointer
* ESP: Stack Pointer、現在の stack の先頭を指す pointer
* EBP: Stack Base Pointer、現在の関数の stack frame の始まりを指す pointer で、この位置の中身にはさらに 1 つ前の関数の EBP の値が格納されている。
* EAX: Accumulator Register、戻り値が格納される register

# FuncA から FuncB (引数 a, b を受け取る) を呼ぶ手順を考える。

## 初期状態 (FuncA から FuncB を呼び出す直前)
```text:初期状態
ESP     -> 0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

## 関数呼び出し、FuncA 側

### 1-1. FuncA 内で、FuncB に渡すための引数を push する
処理: `push b`, `push a`、後ろの引数から push するらしい。
目的: FuncB が参照できるように FuncA 側から引数を stack に書き出しておく。

```text:引数を書き出す
ESP     -> 0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

### 1-2. call 命令により、関数呼び出し後に戻ってくる位置を push する
処理: `call`、call 命令により call 命令 + 4 の位置の code address が push される。stack 領域とは関係ない code 領域の話。その後に EIP が FuncB の先頭に書き換わって処理が始まる。
目的: FuncB の処理が終わった後に、どの位置から処理を再開するかを覚えておくため stack に書き出しておく。

```text:FuncB終了後のFuncAの戻り先を保存
ESP     -> 0x1fd4 (内容の例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

## 関数呼び出し、FuncB 側

### 2-1. あとで復元できるように EBP の値を push して書き出す
処理: `push ebp`
目的: FuncB の終了時に、呼び出し前に FuncA が保持していた EBP の値に戻せるように、FuncA の EBP の値を stack に書き出しておく。

```text:FuncAのEBPの保存
ESP     -> 0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

### 2-2. 自分自身 (FuncB) 用に EBP の値を更新する
処理: `mov ebp, esp`
目的: 2-1 で FuncA 用の EBP を保存したので、次に自分 (FuncB) 用に EBP の値を更新する。

```text:FuncB用のEBPの更新
ESP/EBP -> 0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
           0x2000 (FuncA の stack の始まり)
```

### 2-3. (もし必要であれば) 変数の確保
処理: `sub esp, 4` (変数 32 bits 分の場合)
目的: ESP を -4 することにより、32 bits 分の領域を stack に確保する。
備考: 変数を初期化しない場合、この場所にもともと入っていた値が見える。あと実際には alignment の処理で多めに stack が取られたりもする。

```text:FuncB用変数の確保
ESP     -> 0x1fcc (内容: 変数)
EBP     -> 0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
           0x2000 (FuncA の stack の始まり)
```

### 2-4. 補足
* 変数は EBP - 4 でアクセスする。
* 引数は EBP + 8 でアクセスする。
* FuncB からさらに関数を呼ぶ場合、同様に引数と戻り先が push される。

```text:FuncBからさらに関数が呼ばれた場合
ESP/EBP -> 0x1fc0 (内容: 0x1fd0)
==== v FuncB 領域 / ^ 新関数領域 ====
           0x1fc4 (内容: 戻り先 address)
           0x1fc8 (内容: 引数)
           0x1fcc (内容: 変数)
           0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
           0x2000 (FuncA の stack の始まり)
```

## 関数終了処理、FuncB 側

### 3-1. 戻り値を EAX に入れる
処理: `mov eax`
目的: 戻り値は EAX に入れるという決まり。関数の呼び元は戻ってきた後に EAX を参照して戻り値を受け取る。

### (追記 3-2 と 3-3 の補足
この `mov esp, ebp` と `pop ebp` の 2 つを併せた処理が `leave` 命令。

### 3-2. ESP の値を初期値 (EBP) に戻して FuncB のスタックフレームを破棄する
処理: `mov esp, ebp`
目的: FuncB 用に確保したスタック領域 (スタックフレーム) を捨てて、FuncB 呼び出し初期状態 (正確には EBP を push した直後) にする。

```text:FuncBのスタックフレームの破棄
           0x1fcc (内容: 変数)
ESP/EBP -> 0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
           0x2000 (FuncA の stack の始まり)
```

### 3-3. EBP の値を FuncA 用に書き戻す
処理: `pop ebp`
目的: FuncA に戻るための準備として、EBP を FuncA の値に書き戻す。

```text:FuncAのEBPの復元
           0x1fcc (内容: 変数)
           0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
ESP     -> 0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

### 3-4. FuncA の戻り先に飛ぶ
処理: `ret`、戻り先の値が pop されて EIP が書き換わる。
目的: FuncB 呼び出しの続きから再開する。

```text:FuncAの続きに戻る
           0x1fcc (内容: 変数)
           0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
ESP     -> 0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
           0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

## 関数終了処理、FuncA 側

### 4-1. 引数分の stack を元に戻す -> 初期状態に戻る
処理: `add esp, +8` (引数 a, b それぞれ 32 bits の場合)
目的: 引数分の stack の位置を元に戻す。

```text:FuncAが用意した引数を破棄
           0x1fcc (内容: 変数)
           0x1fd0 (内容: 0x2000)
==== v FuncA 領域 / ^ FuncB 領域 ====
           0x1fd4 (内容例: 0x10000100)
           0x1fd8 (内容: 引数 a)
           0x1fdc (内容: 引数 b)
ESP     -> 0x1fe0 (FuncB を呼び出す直前の stack の先頭)
            ...
EBP     -> 0x2000 (FuncA の stack の始まり)
```

# Ref.
https://vanya.jp.net/os/x86call/#section-6