栈帧结构

这里写图片描述
%esp寄存器存储栈顶地址
%ebp寄存器存储各帧首地址
在x86中%ebp变成了%rbp,%esp变成了%rsp

int son_add(int a, int b)
{
        return a+b;
}

int father()
{
        int a = 8;
        int b = 9;
        int sum = 0;
        sum = son_add(a, b);

        return sum;
}

以这两个函数举例,反汇编代码如下

00000000 <son_add>:
   0:   55                      push   %ebp
   1:   89 e5                 mov    %esp,%ebp
   3:   8b 45 0c            mov    0xc(%ebp),%eax
   6:   03 45 08            add    0x8(%ebp),%eax
   9:   c9                      leave  
   a:   c3                      ret    
   b:   90                      nop    

0000000c <father>:
   c:   55                      push   %ebp
   d:   89 e5                 mov    %esp,%ebp
   f:   6a 02                  push   $0x9
  11:   6a 01                push   $0x8
  13:   e8 fc ff ff ff        call   14 <father+0x8>
  18:   5a                     pop    %edx
  19:   59                     pop    %ecx
  1a:   c9                     leave  
  1b:   c3                     ret    

一开始在未调用son时%ebp这个寄存器所存储的是father的帧首地址。当father调用son函数时首先执行push %ebp,就将%ebp寄存器里所存储的father的帧首地址压入栈中。此时被调用者son的帧首地址就存储着调用者father的帧首地址,而mov %esp %ebp就是将%esp中存储的son的帧首地址赋给%ebp寄存器。此时%ebp寄存器中所存储的就是son的帧首地址了,之后也不再修改。
接下来的两句很有意思,mov 0xc(%ebp),%eax是对帧指针里的地址先增加0xC再取里面的值,增加12是啥意思?12刚好是4的倍数,也就是向上移动三个栈存储单元。根据栈结构图发现,%ebp作为帧首,向上移动一个单元是“返回地址”;向上移动两个单元是参数1,向上移动三个单元当然是参数2!也就是我们传给son_add的第二个参数9。因此这条汇编的意思是把9赋值给%eax寄存器。依次类推是不是还应该把参数1的这个8赋给另一个寄存器呢?编译器可没这么傻,你son_add不就是想做个加法么?直接add 0x8(%ebp),%eax,让%ebp寻找到参数1的地址位置,读取出8,然后直接和%eax的值相加,搞定!
好了,这个时候%eax寄存器就是存有加法结果的寄存器了,计算完成子函数需要返回了,于是先后执行leave和ret,先看leave的等价汇编代码:
leave:movl %ebp,%esp
pop %ebp
这步在理解上稍显困难,主要是对出入栈的操作理解。movl %ebp,%esp这条语句,其实目的就是破坏子函数son_add栈帧结构。想想看,直接修改栈指针%esp,让他指向son_add的帧首,然后执行pop %ebp,将帧首里的值赋给%ebp!回忆下帧首里存的是啥值啊?那当然是father帧首的地址值啊,这句目的就是让%ebp重新指回father栈帧的帧首!OK,son_add的帧首被弹出栈后,栈指针也不会再指向son_add帧首了,而是指向他的上面一个栈存储单元,那就是father帧的末尾:返回地址,leave的使命便完成。接下来就是ret,考虑到ret要完成函数调用的返回,还要维护栈帧的返回,我们可以猜测ret的等价汇编代码应该是:
ret:jmp (%esp)
add $4, %esp
因为此时%esp指向father帧的末尾,而该末尾里面又存储了father调用son_add函数后应该返回的地址(这里应该是18),因此就应该是将该地址取出,直接跳转到18,也就是call语句之后的语句,而子函数son_add的调用既然已经完成,根据个人的猜测,“返回地址”已失去意义,因此栈指针会加4返回。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值