函数(过程)调用机器级表示(一)

过程调用概述

可执行文件装入的时候是映射到存储空间中
用户栈在高地址当中向低地址增长
1
栈底位置确定,栈顶专门放在一个ESP寄存器栈指针寄存器。

  • test.c
 int add(int x,int y){
    
    
 	return x+y;
 }
 int caller(){
    
    
 	int ti = 125;
 	int t2 = 80;
 	int sum = add(t1.t2);//
 	return sum;
 }
  • 执行步骤

  1. P放参数(实参),放入栈中
  2. call指令把返回地址压栈,然后转移到被调用过程去执行
  3. 保存P的现场(即P用到的通用寄存器的内容),并且为自己的局部变量分配空间
  4. 执行Q函数体,此时返回值放到专门的地方
  5. Q恢复P的现场,释放局部变量空间
  6. 取返回地址返回,将控制转移到P
  • 为什么要保存现场?
    因为调用过程和被调用过程用的都是同一套寄存器
  • IA-32的寄存器使用约定

– 调用者保存寄存器:
EAX、EDX、ECX
当过程P调用过程Q时,Q可以直接使用这三个寄存器,不用将它们的值保存到栈中。
如果P在从Q返回后还要用这三个寄存器的话,P应在转到Q之前先保存,并在从Q返回后先恢复它们的值再使用。

– 被调用者保存寄存器:
EBX、ESI、EDI
Q必须先将它们的值保存到栈中再使用它们,并在返回P之前恢复它们的值。


EBP和ESP分别是帧指针寄存器和栈指针寄存器,分别用来指向当前栈帧的底部和顶部。


问题:为减少准备和结束阶段的开销,每个过程应先使用哪些寄存器?
EAX、ECX、EDX!

  • 栈和栈帧的变化

2

  • 上述函数机器级表示

3

4

  • 准备阶段:前三行
    第一条指令总是把就的EBP的值压入栈中
    此时ESP指向此处
    第二条mov指令后,EBP也指向此处,栈底就此形成
    第三条指令减法,ESP-24,栈顶移动,留足空间
  • 分配局部变量:4、5条
    125送到ebp-12的地址
    80送到ebp-8的地址
  • 准备入口参数:6-9条
    80送到ESP+4(即对t2参数赋值)
    125送到esp(即对t1赋值)

注意形参赋值有顺序,从右向左

  • call:
    执行被调用过程,转到add执行
    add的第一二条指令同样是push和mov
    每个函数都相同
  • add准备:
    push EBP在caller中的值
    形成add栈帧底部
    栈底放的是这个函数的调用过程如caller是调用add的过程
    执行结束
  • 返回值总是在EAX中
  • call 返回后总是把下一条指令的地址压入栈中
    返回地址实际为倒数第四行的mov的地址
    当执行ret时候,ret把返回地址取出送到EIP寄存器(PC)
  • 把返回值放入sum
  • return sum 即 把sum的值放到EAX寄存器
  • 结束阶段:最后2条
    恢复现场,leave即退栈
    让ESP等于EBP,中间所有内容丢失
    然后将ESP指向的内容弹出到EBP,即恢复上一个函数的栈底
    然后ESP指向上一个单元即返回地址,返回上一个函数

C函数大致过程

–准备阶段
•形成帧底:push指令 和 mov指令
•生成栈帧(如果需要的话):sub指令 或 and指令
•保存现场(如果有被调用者保存寄存器) :mov指令
–过程(函数)体
•分配局部变量空间,并赋值
•具体处理逻辑,如果遇到函数调用时
–准备参数:
将实参送栈帧入口参数处
–CALL指令:
保存返回地址并转被调用函数
•在EAX中准备返回参数
–结束阶段
•退栈:leave指令 或 pop指令
•取返回地址返回:ret指令


参考:南大计算机系统基础(一)

猜你喜欢

转载自blog.csdn.net/weixin_61631200/article/details/127407334