在函数 a 中调用函数 b。
两个函数如下:
int a()
{
int i=0;
int c;
b(i);
/*b 函数的返回地址指向紧接之后的语句,即 return 0;*/
return 0;
}
int b(int i)
int d=i;
return 0;
{
}
b 函数对应的汇编程序
b:
pushl %ebp //ebp寄存器内容压栈,即保存b函数的上级调用函数的栈基地址
movl %esp, %ebp //esp值赋给ebp,设置b函数的栈基址
subl $4, %esp //分配栈地址给局部变量d
leal 8(%ebp), %eax //将参数i的地址传递给eax,i参数的地址在ebp上方8个字节处,两者中间是b函数的
返回地址和ebp
movl %eax, -4(%ebp) // d = i
movl $0, %eax //返回值0传给eax寄存器
leave //将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
//相当于movl %ebp,%esp popl %ebp
ret //将 a 函数中的“return 0;”语句的地址存入指令寄存器 IP
a 函数执行到调用 b 函数之前时的栈
调用 b(i)执行到 b 函数汇编的 movl %esp, %ebp 语句前时的栈
执行完 subl $4, %esp 语句后的栈
此时 b 的参数 int i 处于%ebp+8 的位置 ,而 b 函数的局部变量 d 处于%ebp-4 的位
置。这里要注意,函数参数压栈按照从右到左的顺序依次进行。
执行 leave 时 esp 指向 ebp 当前位置 ,而 pop ebp 后 栈的分布如下
此时 ip 取 esp 寄存器指向的栈所存的返回地址 ,而 esp+4,即清空了 b 函数除参数以
外的栈,此时如果是以_stdcall 方式调用则 b 函数自动弹出所有参数,如果是以_cdecl
方式调用,则由 a 函数调整堆栈至初始状态。
NJU 顾伟颢