函數呼叫是程式執行的核心機制。本文基於 x86 (32 位) cdecl 呼叫慣例,詳細剖析呼叫指令 call、堆疊幀建立/銷毀、參數右至左壓棧、返回值 EAX 傳遞、leave/ret 返回序列,並提供 objdump 反組譯與 GDB 寄存器觀察實戰紀錄,幫助開發者建立底層執行模型。
call 雙重任務EIP 指向被呼叫函數入口。push ebp ; 保存呼叫者 EBP
mov ebp, esp ; 設定新幀基址
sub esp, N ; 分配區域變數空間 (N bytes)
此時堆疊佈局(高位址→低位址):
[呼叫者 EBP] ← EBP [返回位址] ← EBP+4 [參數 1] ← EBP+8 [參數 2] ← EBP+12 ... [區域變數] ← EBP-x
foo(a, b, c) 先壓 c,再 b,最後 a,使 a 位於 EBP+8 固定偏移,支援可變參數。call 返回後,呼叫者執行 add esp, 12 彈出參數。EAX (ST(0))。mov eax, [ebp-4] ; 取得返回值 (若有)
leave ; 等價於 mov esp,ebp / pop ebp
ret ; 彈出返回位址至 EIP,呼叫者繼續執行
// C 代碼
int bar(int c, int d) { int e = c+d; return e; }
int foo(int a, int b) { return bar(a, b); }
int main() { foo(2, 3); return 0; }
// 編譯:gcc -g -O0 test.c -o test
// 反組譯:objdump -d -M intel -S test
關鍵片段(地址略):
bar:
push ebp
mov ebp, esp
sub esp, 0x10
mov eax, DWORD PTR [ebp+0xc] ; d
mov edx, DWORD PTR [ebp+0x8] ; c
lea eax, [edx+eax] ; c+d
mov DWORD PTR [ebp-0x4], eax ; e
mov eax, DWORD PTR [ebp-0x4] ; 返回值
leave
ret
foo:
push ebp
mov ebp, esp
sub esp, 0x8
mov eax, DWORD PTR [ebp+0xc] ; b
mov DWORD PTR [esp+0x4], eax ; 壓參數 d
mov eax, DWORD PTR [ebp+0x8] ; a
mov DWORD PTR [esp], eax ; 壓參數 c
call bar
leave
ret
main:
push ebp
mov ebp, esp
sub esp, 0x8
mov DWORD PTR [esp+0x4], 0x3 ; 3
mov DWORD PTR [esp], 0x2 ; 2
call foo
mov eax, 0
leave
ret
(gdb) break bar
(gdb) run
(gdb) info registers
esp 0xbffff718
ebp 0xbffff728 ; bar 幀基址
(gdb) up ; 切換到 foo 幀
(gdb) info registers
esp 0xbffff730
ebp 0xbffff738 ; foo 幀基址
(gdb) up ; 切換到 main 幀
(gdb) info registers
esp 0xbffff740
ebp 0xbffff748 ; main 幀基址
觀察:EBP 串連形成呼叫鏈,[EBP] 存放上層 EBP,[EBP+4] 存放返回位址。
mov eax, [ebp-4]:取區域變數 e 至 EAX。leave:mov esp, ebp (棄區域變數) → pop ebp (恢復呼叫者 EBP)。ret:pop eip (跳回呼叫者 call 後指令) → esp+=4。層層 leave/ret 逐級返回至 main。
| 慣例 | 參數順序 | 堆疊清理 | 名稱修飾 | 用途 |
|---|---|---|---|---|
| cdecl | 右→左 | 呼叫者 | _func | C/C++ 預設 |
| stdcall | 右→左 | 被呼叫者 | _func@N | Windows API |
| fastcall | 前兩參 ECX/EDX,其餘右→左 | 被呼叫者 | @func@N | 優化調用 |
| thiscall | this 在 ECX,其餘右→左 | 被呼叫者 | 同上 | C++ 成員函數 |
理解堆疊幀結構、參數佈局、返回序列,是除錯崩潰、分析核心轉儲、編寫組語介面、實現協程/異常處理的基石。建議配合 gcc -S 查看組語、objdump -S 對照源碼、GDB info frame/bt 觀察實時堆疊,深化體感認知。