函数调用过程
本文最后更新于 2025年7月29日 晚上
; =================================================================
; 函数调用的完整汇编执行过程示例
; 演示: int result = add_numbers(10, 20);
; =================================================================
.text
.global _start
_start:
; ===== 程序开始执行,栈初始状态 =====
; ESP = 0x7FFF0000 (假设的栈顶地址)
; EBP = 0x7FFF0000 (帧指针初始化)
main:
; ===== 建立main函数的栈帧 =====
push %ebp ; 保存调用者的帧指针
; ESP = 0x7FFFEFFC, [ESP] = old_EBP
mov %esp, %ebp ; 建立当前函数的帧指针
; EBP = 0x7FFFEFFC
sub $16, %esp ; 为局部变量分配空间(16字节)
; ESP = 0x7FFFEFEC
1 | |
add_numbers:
; ===== 函数序言 - 建立新的栈帧 =====
push %ebp ; 保存调用者(main)的帧指针
; ESP = 0x7FFFEFD C, [ESP] = main的EBP
mov %esp, %ebp ; 建立add_numbers的帧指针
; EBP = 0x7FFFEFDC
sub $8, %esp ; 为局部变量分配空间
; ESP = 0x7FFFEFD4
1 | |
back_to_main:
; ===== 清理参数 (C调用约定: 调用者负责清理) =====
add $8, %esp ; 清理两个参数 (2 × 4字节)
; ESP = 0x7FFFEFEC (回到调用前状态)
1 | |
; =================================================================
; 栈的变化过程总结:
;
; 1. 初始状态: ESP = 0x7FFF0000
; 2. main序言后: ESP = 0x7FFFEFEC
; 3. push 20后: ESP = 0x7FFFEFE8 (参数2)
; 4. push 10后: ESP = 0x7FFFEFE4 (参数1)
; 5. call后: ESP = 0x7FFFEFE0 (返回地址)
; 6. func序言后: ESP = 0x7FFFEFD4 (新栈帧)
; 7. ret后: ESP = 0x7FFFEFE4 (清理栈帧)
; 8. add $8后: ESP = 0x7FFFEFEC (清理参数)
;
; 关键点:
; - push指令: 修改ESP并存储数据
; - call指令: 保存返回地址并跳转
; - 函数序言: 建立新栈帧
; - 函数尾声: 清理栈帧
; - ret指令: 恢复返回地址并跳转
; - 参数清理: 调用者负责(C约定)
; =================================================================