操作系统概论:C语言函数调用及栈帧

参考

c语言函数调用过程原理及函数栈帧分析

背景

栈帧

栈帧本质是一种栈,只是专门用于保存函数调用过程中的参数、返回地址等信息,逻辑上说,每个函数都有自己的栈帧,栈帧是一个函数执行的环境。
寄存器 ebp 内存放栈帧基址,寄存器 esp 内存放栈指针,二者之间的区域称为栈帧。

调用

将主调函数(当前函数)称为 Caller,被调用的函数称为 Callee。
调用时,需要知道:

  • Caller 需要知道 Callee 返回值存在哪里
  • Callee 需要知道Caller传入的参数存在哪里
  • Callee 执行后,返回地址在哪里
  • 返回后,ebp、esp寄存器值应该恢复到调用前的状态

实例

代码

#include <stdio.h>

int Sub(int x,int y)
{
    int t = 0;
    t = x-y;
    return t;
}

int main()
{
    int a = 10;
    int b = 20;
    int c = 0;
    c = Sub(a,b);
    return 0;
}

对应汇编如下

int main()
{
003113F0  push        ebp  
003113F1  mov         ebp,esp 
003113F3  sub         esp,0E4h 
003113F9  push        ebx  
003113FA  push        esi  
003113FB  push        edi  
003113FC  lea         edi,[ebp-0E4h] 
00311402  mov         ecx,39h 
00311407  mov         eax,0CCCCCCCCh 
0031140C  rep stos    dword ptr es:[edi] 
    int a = 10;
0031140E  mov         dword ptr [a],0Ah 
    int b = 20;
00311415  mov         dword ptr [b],14h 
    int c = 0;
0031141C  mov         dword ptr [c],0 
    c = Sub(a,b);
00311423  mov         eax,dword ptr [b] 
00311426  push        eax  
00311427  mov         ecx,dword ptr [a] 
0031142A  push        ecx  
0031142B  call        Sub (31108Ch) 
00311430  add         esp,8 
00311433  mov         dword ptr [c],eax 
    return 0;
00311436  xor         eax,eax 
}
00311438  pop         edi  
00311439  pop         esi  
0031143A  pop         ebx  
0031143B  add         esp,0E4h 
00311441  cmp         ebp,esp 
00311443  call        @ILT+315(__RTC_CheckEsp) (311140h) 
00311448  mov         esp,ebp 
0031144A  pop         ebp  
0031144B  ret             

执行

main函数也是被 _mainCRTStartup 调用的函数,开辟main的栈帧过程如下:

  • ebp esp各自有初始值。
  • ebp值压栈,压到esp的位置,然后esp值赋给ebp。
  • esp值增加,开辟空间

开辟后,3个push压栈了三个寄存器的值,esp 向低地址方向增长3,
,然后用 edi 保存了esp在3个push前的位置, 即初始位置。然后初始化开始时开辟的空间。
初始化局部变量a b c,从ebp的位置往低地址入栈保存,即保存到main函数的栈帧中。

调用sub函数:

  • 形参从右到左入栈:拷贝b和a的值,push到esp的位置,esp增长2。
  • call 指令将 main函数中本应该执行的下一条指令的位置压栈,然后跳转执行函数Sub。
  • 开辟栈帧,同上。

计算:

  • 知道参数有2个,所以ebp+12字节的位置存放第一个参数b,ebp+8字节的位置存放第二个参数a。
  • 作运算,结果存到eax寄存器。

调用结束后返回:

  • 先进行三次出栈,将栈顶值还原到对应edi, esi, ebx寄存器。
  • 移动esp到ebp位置,然后pop ebp,就是取出esp位置的值赋到ebp,值就是之前main函数的ebp,这样ebp就还原到main的栈帧了。
  • ret指令pop了call时压栈的指令位置,程序计数器从此处开始继续执行。
  • esp+8,释放形参a, b。
  • main函数从eax中取出结果,保存到变量c的位置,即接收返回值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值