函数栈帧的创建与销毁

Hello!小伙伴们,很高兴跟大家分享最近我学习到的一些知识,我们都听过一句话,站的更高看的更远,本期我给大家带来通过汇编语言深刻理解函数栈帧的创建与销毁,本期主要解决以下问题:  1.局部变量是怎么创建的?
          2.为什么局部变量的值是随机值?
          3.函数是怎么传参的?传参的顺序是怎样的?
         4.形参和实参是什么关系?
          5.函数调用是怎么做的?
           6.函数调用是结束后怎么返回的?

 tip : 我们这里使用的是vs2020的编译器,对于底层转汇编实现的代码对于不同代码实现是不同的哦!所以我们只能说大同小异,理解本文你可以尝试看看自己的编译器函数栈帧的创建与销毁。

一.轻轻松松-----使用编译器转到反汇编代码

      朋友,我们首先编写一个类似我下面一个简单的程序如加分函数,注意我们尽量让每一步都尽可能详细一些这样理解你的汇编代码就轻松许多咯!

完成主函数之后我们按F11,进入函数的调试环境,然后对着代码右键如下图进入转汇编代码。

  进入这样的地方就是你需要的转汇编代码咯!

二.认认真真--- 学会简单理解这些汇编指令进而掌握函数栈帧创建与销毁

 2.1 一些简单的基本知识

 对于寄存器,rbp,rsp 他们当使用哪个函数他们就会维护哪个函数,以及rbp一般在栈低

为了方便我们理解步骤,我们通常会在汇编指令中再右键去掉符号化,即下图!

一些基本知识我们不过多陈述: 首先要知道

  1.主函数也是其它函数的调用,

  2.函数调用本质是函数栈帧的创建与销毁,这里使用的数据结构是栈,其次cpu对栈的访问优先访问高地址其次访问低地址。

2.2  全力以赴----开始直接理解汇编代码

符号化之后:

理解代码:

tip: 栈下面的是高地址,代码过程中优先使用高地址哦,压栈就相当于像栈中载入一个内存块,寄存器指向rbp,减法 我们要知道寄存器 rsp,rbp用来维护函数的栈帧,rbp指向栈底,rsp指向栈顶哦。 

  最初:要知道其实一开始我们的主函数是这个函数的调用所以这个时候rsp,rbp分别指向这个函数栈帧的栈顶,栈底

  进入代码我们接收到指令,push压栈两个代码块。

这里,rsp 接收到指令sub,栈顶向低地址移动,为了给主函数开辟一个栈,开辟函数的栈帧。

lea 是 "Load Effective Address" 的缩写。在汇编语言中,lea 指令用于计算内存地址并将该地址加载到寄存器中,而不是直接从内存中加载数据。

这里呢call的意思是调用指令,call这里是调用指令。

call 指令:这个指令用于调用一个子程序。在调用时,程序会把当前指令的地址(即下一条指令)压入栈中,以便在被调用的函数执行完毕后能够返回到正确的位置继续执行。

   简单理解呢就是会调用main函数之后呢,call会把调用之后下一个指令压入栈中因为当代码走到函数然后调用完主函数后要返回下一条代码的地址使得程序具有严谨性。这里没有理解没关系,后面有更清晰的例子哦。

这里其实还要对主函数的内存进行初始化,FCCCCCCCC这是导致内存值随机的原因编译器这里没有体现。我们跟着代码继续走接下来对变量指定内存,变量的初始化。

对于函数部分的代码:

 把b指向的数据内容给寄存器edx,把a的数据内容给寄存器ecx。

call 调用指令调用函数add  右边存放了当前执行完当前指令函数部分又回到函数地址当前指令,这样就保证了运行其他函数之后代码又回到当前程序了。

先F11进入函数之前的函数栈中的创建过程

label_name: ; 其他指令 jmp label_name ; 形成一个无限循环

这里是为了给开辟的内存进行初始化:在这里我们的vs2020其实反汇编语言体现的有点复杂了,我在这里简化流程为大家说明这些过程,编译器会根据函数大概需要的内存创建好需要的内存,这里对内存的分配其实看不出来,或者说简化了,然后对于开辟的内存会赋值为FCCCCCCCCCC这如果打印出来就是一个随机值所以你使用局部变量(在函数中)没有赋值的话初始值是FCCCCCCC这个随机值哦。

使用add函数之前我们会将形参的数据用寄存器edx和edx存起来,之后进入add函数我们要移动两个维护函数栈帧的寄存器,是谁呢?对就是rsp跟rbp。

观察到了吗对于这个add函数传参的时候又开辟了内存然后将寄存器ecx edx值赋给他这里就实现了传参,这里我们就可以理解了其实对于实参的传递形参来接收,形参实参变量指向的内存地址是不一样的嘛,所以我们就说形参只不过是实参的拷贝,形参的改变不影响实参.

跟着代码走理解下面的指令:

  将0这个值mov 相当于赋值 开辟两个字节 赋给栈上内存 值为0 继续即可 其中add指令就是说把eax加给ecx 后面的指令都很简单读者自行理解咯。

  这里值得一提的是这个函数是如何返回参数的值的。 看return 对应的代码实际上是一个mov指令把返回值r给了eax 所以返回值其实就是把返回值给了寄存器之后把寄存器的值使用就是我们需要的返回值哦。

   在这里我们可以理解一个基本事实,函数调用之后会把栈中内存销毁如何通过寄存器返回值。

之后的指令

这样rsp和rbp又回到了主函数。

本期让你基本了解汇编指令了,以及较为底层的函数栈帧创建和销毁,以及一些函数中的难题。因为很多小细节我也不是很理解,本文我是按照自己的理解为大家服务的,如果有不妥的地方希望朋友们可以指点一些谢谢!

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值