push/pop/call/ret 動態移動,始終指向棧頂EBP > ESPcall 指令隱含 push eip(返回地址)push ebp ; 保存舊幀基址
mov ebp, esp ; 建立新幀基址
sub esp, N ; 分配區域變量空間(N 為對齊後大小)
[ebp+offset] 訪問參數,[ebp-offset] 訪問區域變量mov esp, ebp ; 恢復棧頂(釋放區域變量)
pop ebp ; 恢復舊幀基址
ret ; 彈出返回地址跳轉
等價於 leave; ret
add esp, param_bytes 清理參數(__cdecl 呼叫約定)swap 調用棧變化
// main.c
void swap(int *a, int *b) { int c = *a; *a = *b; *b = c; }
int main() { int a=16, b=64; swap(&a, &b); return a-b; }
編譯:gcc -g -O0 -fno-omit-frame-pointer -o main main.c
GDB 關鍵斷點與觀察:
(gdb) break main
(gdb) break swap
(gdb) run
(gdb) info frame // 顯示當前棧幀詳情
(gdb) x/20x $esp // 檢查棧內容
(gdb) next // 單步觀察 EBP/ESP 變化
高位址 +------------------+ | main 區域變量 | ← EBP_main +------------------+ | 返回地址 | ← call 指令壓入 +------------------+ | swap 參數 b (64) | ← [EBP_swap+12] | swap 參數 a (16) | ← [EBP_swap+8] +------------------+ | 舊 EBP (main) | ← [EBP_swap+0] ← EBP_swap +------------------+ | swap 區域變量 c | ← [EBP_swap-4] | ... 對齊填充 ... | +------------------+ ← ESP_swap 低位址
| 約定 | 參數入棧順序 | 棧清理者 | 典型場景 |
|---|---|---|---|
| __cdecl | 右→左 | Caller | C/C++ 預設,支援可變參數 |
| __stdcall | 右→左 | Callee | Windows API |
| __fastcall | 前兩參數用寄存器 | Callee | 高效能需求 |
-fomit-frame-pointer:省略 EBP,ESP 直接偏移訪問,除錯困難-O2 內聯小函數,消除棧幀開銷