理解栈帧

顾名思义,“栈”就是相当于”客栈”的意思,在计算机系统中,栈就是数据暂存的一片动态内存。栈可以用来实现函数参数的传递,局部变量的分配和释放,也可以用来储存返回信息,保存寄存器以供恢复调用前处理机状态。

每当调用一个函数,系统会为这个函数分配栈空间,为单个函数分配
的栈空间为栈帧(stack frame)
这里写图片描述
图中寄存器%ebp为帧指针,寄存器%esp为栈指针

假设有一段代码:

#include<stdio.h>

int sum(int a,int b){
    int c=a+b;
    return c;
}
int main(){
    int num1=10;
    int num2=20;
    int s=sum(num1,num2);
    return 0;
}

我们用VS2015进行反汇编得出:

//sum函数的部分指令
int sum(int a, int b) {
010D1690  push        ebp  
    · · ·
010D16C0  ret  
//main函数的部分指令
int main()
{
    · · ·
010D1704  call        sum (010D1023h)  
010D1709  add         esp,8  
    · · ·

我们从上面的反汇编代码可以看到当main函数调用sum函数时执行
call指令我们从后面的地址中可以知道call指令并不能直接跳转到
sum函数,用vs2015按F11跳转到一个新的语句:

00A91023  jmp         sum (0A91690h)  

jmp指令将跳转到sum函数的第一条指令
我们可以知道call指令的效果是将返回地址(紧跟着call指令的下一条指令的地址(0x010D1709))压入栈跳转到被调用函数的栈帧

进入被调用函数的栈帧的时候,首先将调用函数的ebp的值压入栈
当被调用函数一直执行到ret指令时,ret指令弹出返回地址,并跳转到该地址。

我们发现在调用函数和被调用函数都有以下语句:

012D1690  push        ebp  
012D1691  mov         ebp,esp  
012D1693  sub         esp,0CCh  
012D1699  push        ebx  
012D169A  push        esi  
012D169B  push        edi  
012D169C  lea         edi,[ebp-0CCh]  
012D16A2  mov         ecx,33h  
012D16A7  mov         eax,0CCCCCCCCh  
012D16AC  rep stos    dword ptr es:[edi]  
012D1691  mov         ebp,esp 

将栈底下移

012D1693  sub         esp,0CCh 

0cch大小的空间可用于在调试时编辑代码时可能添加的局部变量。

012D1699  push        ebx  
012D169A  push        esi  
012D169B  push        edi  

保存调用函数中寄存器的值

012D169C  lea         edi,[ebp-0CCh]

下移edi到栈顶

012D16A2  mov         ecx,33h  

33h=0cch/4,刚才额外的空间占用33h个4字节空间

012D16A7  mov         eax,0CCCCCCCCh  
012D16AC  rep stos    dword ptr es:[edi] 

初始化刚才的额外的空间

012D16BA  pop         edi  
012D16BB  pop         esi  
012D16BC  pop         ebx  
012D16BD  mov         esp,ebp  
012D16BF  pop         ebp  
012D16C0  ret  

当ret指令之前,ebx,esi,edi恢复原来的值,栈顶上移,恢复原来栈帧的ebp。

还有个问题就是,当我们在传递参数时,参数列表是从右往左顺序压栈的,这是为什么呢?

012D16FC  mov         eax,dword ptr [num2]
012D16FF  push        eax  
012D1700  mov         ecx,dword ptr [num1]  
012D1703  push        ecx  

这就引出c语言的一个特性了——可变长参数

c语言入栈从右往左,栈底的元素就是参数列表中最右边的一个,我们只需知道第一个参数的位置,剩下到栈底都是可变参数,假若从左往右,我们则不能知道最右边的参数位置,也就无法实现可变参数了。

c语言中的printf就是可变参数函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值