C program
了解です。では、先ほどの Cコード例 (add と main) を使って、スタック上の配置とアセンブラ命令の対応 を一緒に整理します。
これで「Cの変数・引数がどうスタックに置かれているか」が一目でわかります。
1. Cコード
int add(int a, int b) {
int c = a + b;
return c;
}
int main() {
int result = add(3, 4);
return result;
}
2. スタックのイメージ
add関数呼び出し直後
高アドレス
-------------------
戻りアドレス ← call add で自動的に push
-------------------
引数 b [ebp+12] ← スタックに積まれた順番
-------------------
引数 a [ebp+8]
-------------------
旧EBP [ebp] ← push ebp
-------------------
ローカル変数 c [ebp-4]
-------------------
低アドレス
main関数のスタック(add呼び出し前)
高アドレス
-------------------
戻りアドレス ← call main で push
-------------------
旧EBP [ebp] ← push ebp
-------------------
ローカル変数 result [ebp-4]
-------------------
低アドレス
3. アセンブラ命令とスタック対応
add関数
| 命令 | 役割 | スタック上の意味 |
|---|---|---|
push ebp |
前のEBPを退避 | スタックに旧EBPを積む |
mov ebp, esp |
フレームポインタ設定 | EBPを新しいフレームの基準に |
mov eax, [ebp+8] |
aをeaxにロード | 引数aを読み込む |
add eax, [ebp+12] |
bをeaxに加算 | 引数bを読み込んで加算 |
pop ebp |
フレームポインタ復元 | スタックを元に戻す |
ret |
呼び出し元に戻る | スタック上の戻りアドレスにジャンプ |
main関数
| 命令 | 役割 | スタック上の意味 |
|---|---|---|
push ebp |
前のEBPを退避 | スタックに旧EBPを積む |
mov ebp, esp |
フレームポインタ設定 | EBPを新しいフレームの基準に |
sub esp, 4 |
ローカル変数確保 | result用に4バイト確保([ebp-4]) |
push 4 |
addの第2引数 | スタックに積む |
push 3 |
addの第1引数 | スタックに積む |
call add |
add関数呼び出し | リターンアドレスをpushしてジャンプ |
add esp, 8 |
引数削除 | pushした引数2つ分をスタックから削除 |
mov [ebp-4], eax |
resultに保存 | addの戻り値をローカル変数resultに書き込む |
mov eax, [ebp-4] |
return result | 戻り値としてeaxにロード |
pop ebp |
フレームポインタ復元 | スタックを元に戻す |
ret |
呼び出し元に戻る | スタック上の戻りアドレスにジャンプ |
4. ポイントまとめ
-
引数 → スタック上で
[ebp+8], [ebp+12], ... -
ローカル変数 →
[ebp-4], [ebp-8], ... -
戻り値 →
eaxに格納 -
関数呼び出し時のスタックの流れ
-
引数を積む → callで戻りアドレスが積まれる → calleeがフレームを作る → 処理 → retで戻る
-
-
フレームポインタ(EBP) はローカル変数・引数を固定オフセットで参照するための目印
💡 補足:
-
スタックは LIFO(後入れ先出し)
-
pushで上に積む、popで取り出す -
espは常に「スタックの一番上」、ebpは「フレームの基準」
C program
了解です。では、先ほどの Cコード例 (add と main) を使って、スタック上の配置とアセンブラ命令の対応 を一緒に整理します。
これで「Cの変数・引数がどうスタックに置かれているか」が一目でわかります。
1. Cコード
int add(int a, int b) {
int c = a + b;
return c;
}
int main() {
int result = add(3, 4);
return result;
}
2. スタックのイメージ
add関数呼び出し直後
高アドレス
-------------------
戻りアドレス ← call add で自動的に push
-------------------
引数 b [ebp+12] ← スタックに積まれた順番
-------------------
引数 a [ebp+8]
-------------------
旧EBP [ebp] ← push ebp
-------------------
ローカル変数 c [ebp-4]
-------------------
低アドレス
main関数のスタック(add呼び出し前)
高アドレス
-------------------
戻りアドレス ← call main で push
-------------------
旧EBP [ebp] ← push ebp
-------------------
ローカル変数 result [ebp-4]
-------------------
低アドレス
3. アセンブラ命令とスタック対応
add関数
| 命令 | 役割 | スタック上の意味 |
|---|---|---|
push ebp |
前のEBPを退避 | スタックに旧EBPを積む |
mov ebp, esp |
フレームポインタ設定 | EBPを新しいフレームの基準に |
mov eax, [ebp+8] |
aをeaxにロード | 引数aを読み込む |
add eax, [ebp+12] |
bをeaxに加算 | 引数bを読み込んで加算 |
pop ebp |
フレームポインタ復元 | スタックを元に戻す |
ret |
呼び出し元に戻る | スタック上の戻りアドレスにジャンプ |
main関数
| 命令 | 役割 | スタック上の意味 |
|---|---|---|
push ebp |
前のEBPを退避 | スタックに旧EBPを積む |
mov ebp, esp |
フレームポインタ設定 | EBPを新しいフレームの基準に |
sub esp, 4 |
ローカル変数確保 | result用に4バイト確保([ebp-4]) |
push 4 |
addの第2引数 | スタックに積む |
push 3 |
addの第1引数 | スタックに積む |
call add |
add関数呼び出し | リターンアドレスをpushしてジャンプ |
add esp, 8 |
引数削除 | pushした引数2つ分をスタックから削除 |
mov [ebp-4], eax |
resultに保存 | addの戻り値をローカル変数resultに書き込む |
mov eax, [ebp-4] |
return result | 戻り値としてeaxにロード |
pop ebp |
フレームポインタ復元 | スタックを元に戻す |
ret |
呼び出し元に戻る | スタック上の戻りアドレスにジャンプ |
4. ポイントまとめ
-
引数 → スタック上で
[ebp+8], [ebp+12], ... -
ローカル変数 →
[ebp-4], [ebp-8], ... -
戻り値 →
eaxに格納 -
関数呼び出し時のスタックの流れ
-
引数を積む → callで戻りアドレスが積まれる → calleeがフレームを作る → 処理 → retで戻る
-
-
フレームポインタ(EBP) はローカル変数・引数を固定オフセットで参照するための目印
💡 補足:
-
スタックは LIFO(後入れ先出し)
-
pushで上に積む、popで取り出す -
espは常に「スタックの一番上」、ebpは「フレームの基準」