从汇编层面看函数调用的过程

栈帧

C语言中,每个栈帧对应着一个未运行完的函数,栈中保存了一个函数调用所需要维护的信息,这常常被称为堆栈帧或活动记录。

堆栈帧包括的内容

  • 函数的返回地址和参数
  • 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
  • 保存的上下文:包括在函数调用前后需要保持不变的寄存器

我们知道每一次函数调用都是一个过程,一个函数的活动记录用ebp和esp这两个寄存器划定范围

  • ebp(帧指针):存放了指向函数栈帧栈底的地址
  • esp(栈指针):存放了指向函数栈帧栈顶的地址

函数调用

函数调用步骤:

  • 参数入栈:按照函数调用惯例将所有或一部分参数压入栈中,如果有其他参数没有入栈,那么使用特定的寄存器传递
  • 返回地址入栈: 将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
  • 代码跳转: 跳转到被调用函数的函数体执行

函数体的标准开头:

  • push ebp:将调用者的ebp压栈处理,保存指向栈底的ebp的地址,方便函数返回之后的现场恢复
  • mov ebp,esp:将当前栈帧切换到新栈帧(将esp值装入ebp,更新栈帧底部), 这时ebp指向栈顶,此时栈顶就是old ebp
  • sub esp,XXX:在栈上分配XXX字节的临时空间
  • push XXX:如有需要,保存名为XX的寄存器(可保存多个)

函数调用的参数显然存储在函数调用者的栈帧中,不是被调用函数的栈帧中

函数返回

函数返回步骤:

  • mov eax, xxx保存被调用函数的返回值到 eax 寄存器中
  • mov esp,ebp:恢复esp,回收局部变量空间
  • pop ebp: 将上一个栈帧底部位置恢复到 ebp
  • ret:弹出当前栈顶元素,从栈中取到返回地址,并跳转到该位置

gdb反汇编出来的代码主要分为3个部分

  • 指令地址
  • 指令相对于当前函数起始地址以字节为单位的偏移
  • 指令

举例分析

#include<stdio.h>
 
 int sum(int a,int b)
  {
   
      int s=a+b;
      return s;
  }
  
  
  int main(int argc,char *argv[])
  {
   
    int n=sum(1,2);
    printf("n=%d\n",n);
 
    return 0;
  }

0x080483db <+0>:	push   %ebp

需要说明的是:gdb反汇编输出的结果中的指令地址和偏移只是gdb为了让我们更容易阅读代码而附加上去的,保存在内存中的以及被CPU执行的代码只有上图中的指令部分

   0x080483db <+0>:	push   %ebp
   0x080483dc <+<
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值