函数调用栈帧,函数调用栈帧包含哪些内容

  函数调用栈帧,函数调用栈帧包含哪些内容

  Yyds干货库存

  前言模块接近C语言的边界,需要一段时间学习。但是,当我们知道了这些知识,我们在C语言的函数中所能看到的不仅仅是外观,还有函数是如何调用的。然而,我的能力有限。以下知识如有不当,请指正。

  知识点储备了对函数的初步了解(这里说的函数默认是用户自定义函数)。了解C程序地址空间的基本寄存器。了解汇编语言函数的一些概念功能。大家应该都不陌生,这里就不细说了。让我们看一看。

  ret_type fun_name(para1,*)

  {

  声明;//语句项

  }

  Ret_type返回类型

  Fun_name函数名

  Para1函数参数C程序地址空间(关键内存)我们一直说:“全局变量的生命周期就是它们所在的整个程序”,“静态修改变量的生命周期越来越长”,“最重要的临时变量都要被销毁了”。但我们需要知道这是怎么回事。在C语言中,我们创建的每个变量都会有自己的存储类别,就像汽车一般不会停在高楼里,所有的东西都会有自己的集合。

  看一看代码来验证它。

  #包含stdio.h

  #包含stdlib.h

  int g _ val1=10

  int g _ val2=10

  int g _ val3

  int g _ val4

  int main()

  {

  const char * str= abcdef

  printf(code: %p\n ,main);

  printf(只读:%p\n ,str);

  printf(init g_val1 : %p\n ,g _ val 1);

  printf(init g_val2 : %p\n ,g _ val 2);

  printf(uninit g_val2 : %p\n ,g _ val 3);

  printf(uninit g_val2 : %p\n ,g _ val 4);

  char * P1=(char *)malloc(sizeof(char *)* 10);

  char * p2=(char *)malloc(sizeof(char *)* 10);

  printf(堆地址:%p\n ,P1);

  printf(堆地址:%p\n ,p2);

  printf(stack addr : %p\n ,str);

  printf(stack addr : %p\n ,P1);

  printf(堆栈地址:%p\n ,p2);

  返回0;

  }

  可以看出,局部变量存储在堆栈上,堆栈空间向低位地址方向打开。

  相关寄存器函数的调用与CPU中的寄存器有很大关系。下面是一些基础知识。

  Eax:通用寄存器,保存临时数据。常用于返回值ebx:通用寄存器,保存临时数据ebp:底部寄存器esp:顶部寄存器eip:指令寄存器,保存当前指令下一条指令的地址。相关汇编语言mov:数据传输指令push:数据放入堆栈,esp堆栈的顶层寄存器也需要改变。pop:数据弹出到指定位置。同时,esp栈顶的寄存器也要改变。sub:减法命令add:加法命令调用:函数调用,1。输入寄信人地址,请按2。跳转到目标函数:ret:恢复返回地址,推入eip,类似于pop eip命令。看了这么多知识,一定觉得很枯燥,认为这和函数栈框架无关。别担心,给你

  这里的栈帧,为了方便理解,我们这样看栈空间,所以会多画一些图。

  我们知道主函数也是函数,也可以被调用,所以主函数也会形成堆栈框架。

  代码示例

  int MyAdd(int a,int b)

  {

  int c=a b;

  返回c;

  }

  int main()

  {

  int x=0xA

  int y=0xB

  int z=0;

  z=MyAdd(a,b);

  printf(z=%d\n ,z);

  返回0;

  }转到反汇编,打开寄存器。

  我复制了汇编代码,我们一步步分析这些东西。

  int main()

  {

  int main()

  {

  int main()

  {

  00821E40推送ebp

  00821E41 mov ebp,esp

  00821E43子esp,0E4h

  00821E49推送ebx

  00821E4A推送esi

  00821E4B推送edi

  00821E4C lea edi,[ebp-24h]

  00821E4F mov ecx,9

  00821E54 mov eax,0CCCCCCCCh

  00821 e59 rep stos dword ptr es:[EDI]

  00821E5B mov ecx,82C003h

  00821E60电话0082130C

  int x=0xA

  00821E65 mov dword ptr [ebp-8],0Ah

  int y=0xB

  00821E6C mov dword ptr [ebp-14h],0Bh

  int z=0;

  00821E73 mov双字指针[ebp-20h],0

  z=MyAdd(x,y);

  00821E7A mov eax,dword ptr [ebp-14h]

  00821E7D推送eax

  00821E7E mov ecx,dword ptr [ebp-8]

  00821E81推送ecx

  00821E82致电008211E5

  00821E87添加esp,8

  00821E8A mov dword ptr [ebp-20h],eax

  printf(z=%d\n ,z);

  00821E8D mov eax,dword ptr [ebp-20h]

  00821E90推送eax

  00821E91推送827BCCh

  00821E96致电008213A2

  00821E9B添加esp,8

  返回0;

  00821E9E xor eax,eax

  }

  00821EA0 pop edi

  00821EA1 pop esi

  00821EA2 pop ebx

  00821EA3添加esp,0E4h

  00821EA9凸轮轴位置ebp,esp

  00821EAB电话00821235

  00821EB0 mov esp,ebp

  00821EB2 pop ebp

  0821eb3ret EBP指向堆栈底部,esp指向堆栈顶部,eip指向下一个要执行的地址。第一步int x=0xA还没有执行;

  01011E65 mov dword ptr [ebp-8],0Ah

  //在ebp-8打开一个空间,把x的值放进去

  int y=0xB

  01011E6C mov dword ptr [ebp-14h],0Bh

  //在ebp-14打开一个空间,把Y的值放进去

  int z=0;

  00821E73 mov双字指针[ebp-20h],0

  //在ebp-20处开辟一个空间,把Z的值放进去

  可以看出,X,Y,Z的空间是不连续的,这是一种VS保护机制,防止一些程序员猜测对应的地址。

  步骤2 00821E7A MOV eax,DWORD PTR [ebp-14H]将EBP-14(即Y)分配给eax。

  Eax是一个临时寄存器,它保存临时数据,通常用于返回值。

  00821E7D push eaxpush命令将eax的值放入栈中,同时栈顶位置变化4个字节,因为Y是int类型。

  推送后堆栈顶部

  0821e7emov ecx,Dword PTR [ebp-8]将ebp-8(也就是X)分配给ecx。

  0821E81Push ecx同上。ECX的值被压入堆栈,堆栈顶部的位置发生变化。

  结论临时变量(自变量的临时副本)的形成是在函数调整之前完成的。参数实例化的顺序是从右到左,参数的空间与调用函数相邻。先说通话命令的功能。

  按回邮地址(最重要的)进入目标函数按回邮地址,谁?为什么要压进去?向谁施压?按下一个命令的地址。

  为什么要压进去?根本原因是函数被调用后,可能需要返回00821E82调用008211E5。

  Jump命令改变eip,转移到目标函数,并调用它。

  Jmpqian

  Jmp侯

  现在我们终于进入了MyAdd()函数,画出我们的堆栈框架图。

  由于篇幅有限,先说到这里,下一个再接着讲MyAdd()函数内部的东西。

  示例代码int MyAdd(int a,int b)

  {

  int c=0

  c=a b;

  返回c;

  }

  int main()

  {

  int x=0xA

  int y=0xB

  int z=0;

  z=MyAdd(x,y);

  printf(z=%d\n ,z);

  返回0;

  }今天的汇编语言

  int MyAdd(int a,int b)

  {

  001E2EC0推送ebp

  001E2EC1 mov ebp,esp

  001E2EC3子esp,0CCh

  001E2EC9推送ebx

  001E2ECA推送esi

  001E2ECB推送edi

  001E2ECC lea edi,[ebp-0Ch]

  001E2ECF mov ecx,3

  001E2ED4 mov eax,0CCCCCCCCh

  001 e 2 ed 9 rep stos dword ptr es:[EDI]

  001E2EDB mov ecx,1EC003h

  001E2EE0呼叫001E130C

  int c=0;

  001E2EE5 mov双字指针[ebp-8],0

  c=a b;

  001E2EEC mov eax,dword ptr [ebp 8]

  001E2EEF add eax,dword ptr [ebp 0Ch]

  001E2EF2 mov dword ptr [ebp-8],eax

  返回c;

  001E2EF5 mov eax,dword ptr [ebp-8]

  }

  001E2EF8 pop edi

  001E2EF9 pop esi

  001E2EFA pop ebx

  001E2EFB添加esp,0CCh

  001E2F01凸轮轴位置ebp,esp

  001E2F03呼叫001E1235

  001E2F08 mov esp,ebp

  001E2F0A pop ebp

  01e2f0bret我们的堆栈框架图

  MyAdd函数栈框架形成的第一步是00821740 push ebp。这个命令是把ebp(也就是栈底)的内容压入栈中,栈顶也是变化的。

  步骤2 mov:数据传输指令

  0821741 MOVebp,esp该命令的意思是将ESP的内容覆盖到ebp。

  esp的内容直接覆盖ebp的内容。这个过程不通过内存,而是直接通过CPU。

  那么我们可能会想,栈底呢?我们不能把它拿回来吗?其实不是的。上一步我们不是保存了栈底的内容吗!

  步骤3 sub:减法命令

  00821743子esp,0CCh

  //0CCh的大小和你定义的函数大小有关。这个命令意味着esp减去某个值,结果放入esp。

  至此,我们差不多形成了MyAdd()的堆栈框架。

  第四步int c=0;

  001E2EE5 mov双字指针[ebp-8],0

  //在ebp-8处开辟一个空格,把C的值放进去,和开辟main的变量是一样的。

  第五步c=a b;

  001E2EEC mov eax,dword ptr [ebp 8]

  001E2EEF add eax,dword ptr [ebp 0Ch]

  01e2f2mov dword ptr [EBP-8],EAX一一分析

  01e2eeec mov eax,dword ptr [ebp 8]把ebp 8放在eax,那么ebp 8是什么?答案是我们x值的一个副本。

  以同样的方式;以类似的方式

  01e2eeef add eax,dword ptr [ebp 0Ch]这个命令是将ebp 0Ch和eax的内容加入eax,ebp 0Ch是y值的副本。

  01e2eef2mov dword ptr [ebp-8],eax这个命令是把eax写入ebp-8,也就是c

  准备返回第一步001e2ef5moveax,dword ptr [EBP-8]保存返回值。

  步骤2 001E2F08 mov esp,ebp涵盖ebp到esp。这一步也可以称为“释放堆栈帧”

  第三步:pop:数据弹出到指定位置,esp栈顶寄存器也发生变化。

  001E2F0A pop ebp“反弹栈”将ebp中主函数的栈底,esp内容发生变化。

  第4步ret:恢复返回地址,按eip,类似于pop eip命令。

  01e2f0bret把上一次调用的下一个地址放在eip中,esp的内容发生了变化。

  步骤5释放临时复制的变量。

  01e1e87add添加esp,8表示esp 8放在esp里。

  现在我们又回到了MyAdd被执行死刑之前。

  步骤6: 001e1e8amov dword ptr [ebp-20h],eax接收返回值,将EAX的值放入EBP-20(即Z)

  回归…的本质

  返回给main的堆栈帧被返回给相应的代码。汇总函数的堆栈框架由编译器决定。推入的变量空间是连续的。0CCh的大小与您定义的函数的大小有关。为什么函数的堆栈帧是由编译器决定的?我们的C语言有很多数据类型,也就是我们的编译器有能力知道所有类型变量的大小。

  我可能已经说了所有关于堆栈帧的内容。我看看以后还需不需要补充一些知识。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: