函数栈桢:
从高地址到低地址依次是:
函数参数
返回地址
ebp(ebp始终指向栈中存放eip的地址减去4的地址处。此处的eip指的调用函数的的返回地址。)
局部对象
下面分析一段代码的过程:
int g(int x,int y)
{
int c=1;
return x +y+ c;
}
int f(int x)
{
int a=5;
int b=4;
return g(a,b);
}
int main(void)
{
return f(8) + 1;
}
对应的汇编代码(去掉链接信息):
g:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $1, -4(%ebp)
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %eax, %edx
movl -4(%ebp), %eax
addl %edx, %eax
leave
ret
f:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $5, -4(%ebp)
movl $4, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, 4(%esp)
movl -4(%ebp), %eax
movl %eax, (%esp)
call g
leave
ret
main:
pushl %ebp
subl $4, %esp
movl $8, (%esp)
call f
addl $1, %eax
leave
ret
call f: 等价于 push %eip; mov f,%eip; (返回地址入栈,跳转执行)
enter :pushl %ebp; movl %esp, %ebp(函数栈桢入栈)
leave:等价于 movl %ebp,%esp; pop %ebp;恢复调用函数esp,栈桢出栈
ret:等价于 popl %eip;(跳转执行)
函数调用过程:
首先函数参数入栈:
对应代码:
subl
4,movl
8, (%esp)
接着:函数返回地址入栈
call f;
接着:ebp入栈(好处:函数参数可以通过ebp取)
如取最左边参数:就是8(%ebp)
最后函数局部对象入栈;