从汇编语言角度理解C语言栈帧

在C语言的调用约定中,栈是现实函数的局部变量、参数和返回值地址的关键因素。

函数执行前、执行过程以及执行后程序分别干了什么事情?

在整个过程中唯一不变的就是基址指针寄存器,位于返回地址和函数变量之间,通过+N*4(%ebp)可以访问函数参数和返回地址,通过-N*4(%ebp)可以访问局部变量。

函数执行前:
程序将函数的参数逆序压栈,接着发出一条Call指令,表明程序希望开始执行哪一个函数。Call指令完成两件事情:首先,将下一条指令的地址即返回地址压入栈中;然后,修改指令指针(%eip)以指向函数起始处。
此时的栈:
参数N
...
参数2
参数1
返回地址<--(%esp)
函数开始时:
首先,通过pushl %ebp指令保存当前的基址指针寄存器%ebp,基址指针寄存器,是一个特殊的寄存器,用来访问函数的参数的局部变量。其次,他会用movl %esp, %ebp 将基址指针设为栈指针。你会发现(%ebp)含有旧的%ebp,4(%ebp)含有返回地址,8(%ebp)是函数的第一个参数的位置。
此时的栈:
参数N
...
参数2
参数1          <--8(%ebp)
返回地址     <--4(%ebp)
旧%ebp       <--(%ebp)和(%esp)
接下来,函数为其所需的局部变量保存栈空间,只需将栈指针向下移动即可,例如,sub $8, %esp
这样就把变量放在了栈帧上,但函数返回时,栈帧不复存在。这就是局部变变量的原因。

此时的栈:
参数N
...
参数2
参数1          <--8(%ebp)
返回地址     <--4(%ebp)
旧%ebp       <--(%ebp)和(%esp)
局部变量1     <-- -4(%ebp)
局部变量2     <-- -8(%ebp)
所以我们通过使用(%ebp)的不同偏移量,通过基址寻址访问这个函数所需的所有数据。
%ebp正是专门为了这一目的设计的,这是它被称为基址指针的原因。

全局变量和静态变量的唯一区别就是静态变量只用于一个函数,而全局变量可有许多函数共同使用。

函数执行完毕后,函数做三件事情:
(1)将返回值-4(%ebp)存入%eax(返回值始终存储在%eax中),linux要求退出码保存在%ebx中,所以我们可以在主函数中movl %eax, %ebx,
(2)将栈恢复到调用函数时的状态 movl %ebp, %esp pop1 %ebp ret
(3)将控制权返回给调用它的程序。通过ret指令将栈顶的值弹出,并将指令指针寄存器%eip设置为该弹出器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值