函数栈帧,也可以称为函数调用栈帧,是计算机在执行函数时为其分配的一块内存区域。每当一个函数被调用时,一个新的栈帧就会被创建并压入调用栈中。...函数栈帧的销毁 当函数执行完毕并准备返回时,其对应的栈帧就会被销毁。销毁过程主要包括以下几个步骤: 释放内存:将栈帧所占用的内存释放回系统。...ebp寄存器的值被用作参考点,用于定位栈上的局部变量和参数。 ebp一般被用来指向当前函数的栈帧的底部。栈帧是指在函数调用时,函数的局部变量、参数和返回地址等保存在栈上的一块连续的内存空间。...当一个函数被调用时,它的局部变量和函数参数等信息会被压入栈中。ESP寄存器指向栈顶的地址,即最后被压入栈的数据所在的内存地址。使用ESP寄存器,可以轻松地在栈上分配和释放内存。...在函数执行过程中,ESP寄存器会不断向下移动,以便为新的局部变量分配空间。当函数返回时,ESP寄存器会回到调用函数前的位置,以释放栈上的内存空间。
函数通过栈来实现控制转移、参数传递、局部变量的分配和释放3个功能。 计算机有专门的一块内存区域作为栈,每个函数都可以在栈上申请一块内存区域作为函数的存储空间,而该存储空间则被称为函数的栈帧。...编写代码 详细解释栈帧创立和销毁过程 如下图所示,在栈区(计算机专门的内存空间),每个函数在栈区申请一块内存空间,称为函数栈帧。在调用哪个函数,esp和ebp就跑去维护哪个函数的栈帧。...esp指向移动8个字节,上面的空间不属于。再把eax的值放到ebp-20h当中。eax的值就是出add函数时委托到eax当中的和,和放到局部变量c当中,这样返回值就带回来了。...函数调用时参数时如何传递的? 当没有调用函数的时候已经pushpush把两个参数从右向左开始压栈压进去了,当真的进入形参函数的时候,其实在add函数栈帧里,通过指针的偏移量找回了形参。...函数的返回值是如何带会的? 调用之前就把call指令的下一条指令的地址记住了,当往回返的时候,就可以跳转到call指令下一条指令的地址,返回值是通过寄存器的方式调用回来的。 欢迎交流!
大家都知道函数调用是通过栈来实现的,而且知道在栈中存放着该函数的局部变量。但是对于栈的实现细节可能不一定清楚。本文将介绍一下在Linux平台下函数栈是如何实现的。...栈帧的结构 函数在调用的时候都是在栈空间上开辟一段空间以供函数使用,所以,我们先来了解一下通用栈帧的结构。...函数栈空间主要是由这两个寄存器来确定的。 当程序运行时,栈指针rsp可以移动,栈指针和帧指针rbp一次只能存储一个地址,所以,任何时候,这一对指针指向的是同一个函数的栈帧结构。...而帧指针rbp是不移动的,访问栈中的元素可以用-4(%rbp)或者8(%rbp)访问%rbp指针下面或者上面的元素。...首先,函数栈上开辟了16字节的空间,存储定义的3个int型变量,建立了main函数的栈。 接着,会给三个变量进行赋值。 以下4行代码是进行参数传递。
也就是说寄存器 %ebp 为帧指针,寄存器 %esp 为栈指针。 当程序执行时,栈指针可以移动,因此大多数信息的访问都是相对于帧指针的。 ? ...各位需要知道的是,每一个栈帧都建立在调用者的下方(也就是地址递减的方向),当被调用者执行完毕时,这一段栈帧会被释放。...还有一点很重要的是,%ebp和%esp的值指示着栈帧的两端,而栈指针会在运行时移动,所以大部分时候,在访问存储器的时候会基于帧指针访问,因为在一直移动的栈指针无法根据偏移量准确的定位一个存储器位置。 ...相反,如果将栈指针加上一定的值,也就是向上移动,那么就相当于压缩了栈帧的长度,也就是说内存被释放了。需要注意的是,上面的一切内容,都基于一个前提,那就是帧指针在过程调用当中是不会移动的。...pushl %ebp movl %esp, %ebp ②、建立起来的栈帧就是为被调用者准备的,当被调用者使用栈帧时,需要给临时变量分配预留内存,这一步一般是经过下面这样的汇编代码处理的。
当函数调用另一个函数时,调用者会将一些数据(如函数参数)压入堆栈中,ESP寄存器会随之向下移动,指向新的堆栈顶部。在函数返回后,又会通过调整ESP寄存器的值来释放堆栈空间。 4....当函数被调用时,编译器会在栈上动态创建函数栈帧,并在其中分配存储局部变量和参数的空间。...当Add函数被调用时,编译器会执行以下步骤来创建函数栈帧: 1. 首先,编译器将函数的返回地址和旧的栈帧指针(EBP)保存在栈上。 2....调整栈顶指针 紧接着,通过执行 MOV 指令,让栈顶指针(ESP)指向 EBP 原先的指向位置。这样做的目的是为了释放函数栈帧所占用的内存空间。 3....当调用函数时,在调用之前,用push把参数从右向左压栈, 当进入形参函数时,在函数的栈桢里通过指针偏移量找到形参 4.形参和实参是什么关系?
在函数执行过程中,栈指针esp会随着数据的入栈和出栈而移动,因此函数中对大部分数据的访问都基于帧指针%ebp进行。?对于函数A调用函数B的情况,传递给B的参数包含在A的栈帧中。...当A调用B时,函数A的返回地址(调用返回后继续执行的指令地址)被压入栈中,栈中该位置也明确指明了A栈帧的结束处。而B的栈帧则从随后的栈部分开始,即图中保存帧指针(ebp)的地方开始。...调用指令CALL的作用是把返回地址压入栈中并且跳转到被调用函数开始处执行。返回地址是程序中紧随调用指令CALL后面一条指令的地址。因此当被调函数返回时就会从该位置继续执行。...(执行完此指令后,栈指针esp会向下移动,指向保存原ebp的空间的地方)4 movl %esp,%ebp (把数据(原ebp的值)压入栈后,栈指针esp会向下移动4个字节指向放原ebp的值的空间,执行此句指令的意义就...从以上分析可知,C语言在调用函数时是在堆栈上临时存放被调函数参数的值,即C语言是传值类语言,没有直接的方法可用来在被调用函数中修改调用者变量的值。
为什么创建局部变量时如果不初始化,局部变量的值会是随机值? 函数是怎么传参的?传参的顺序又是什么? 形参和实参有什么关系? 函数是如何调用的? 调用结束后又是如何返回的?...*[bp]: 基址指针寄存器 *[sp]: 堆栈指针寄存器 *[ebp]: 栈底指针 *[esp]: 栈顶指针 接下来我们就通过下面这个代码来介绍一下 ebp 和 esp 它们是如何创建和维护函数栈帧的...——四个字节 //dword ptr——内存单元为四个字节的长度 //ebp-8——地址名 //2——移动对象 这里的意思就是将2这个值移动到ebp-8这个地址上; 变量b dword ptr [ebp...-14h],3 //dword——四个字节 //dword ptr——内存单元为四个字节的长度 //ebp-14h——地址名 //3——移动对象 这里的意思就是将3这个值移动到ebp-14h这个地址上;...,这样我们在调用完函数后能够通过pop指令在释放Add函数的函数栈帧时找到main函数的栈底地址。
| <----- esp |----------------------| low address 三、x86函数调用 当需要调用另一个函数时...当调用函数发生时,caller执行逻辑会跳转到callee,拿到结果后,在跳转会caller。这就需要改变下面几个寄存器的值: eip指令指针,需要改成指向callee的指令。...ebp 和 esp 当前分别指向caller栈帧的顶部和底部。两个寄存器都需要更新为 指向callee的新栈帧的顶部和底部。 当函数返回时,需要恢复寄存器中的旧值,才可以返回caller。...请注意,当我们将参数压入堆栈时,esp 会递减。参数以相反的顺序压入堆栈。(上面是高地址) step2:旧的eip入栈 旧的eip(rip)压入堆栈。...step4:将旧的ebp入栈 step5:ebp向下移动指向新栈帧顶部 这就是mov %esp %ebp的含义: step6:esp向下移动 通过sub esp(esp地址–) 来为新栈帧分配新空间
| <----- esp |----------------------| low address 三、x86函数调用 当需要调用另一个函数时...ebp 和 esp 当前分别指向caller栈帧的顶部和底部。两个寄存器都需要更新为 指向callee的新栈帧的顶部和底部。 当函数返回时,需要恢复寄存器中的旧值,才可以返回caller。...请注意,当我们将参数压入堆栈时,esp 会递减。参数以相反的顺序压入堆栈。(上面是高地址) image.png step2:旧的eip入栈 旧的eip(rip)压入堆栈。...image.png step4:将旧的ebp入栈 step5:ebp向下移动指向新栈帧顶部 这就是mov %esp %ebp的含义: image.png step6:esp向下移动 通过sub...image.png step11:从堆栈中删除参数 继续讲堆栈上的参数弹出到寄存器,然后删除esp栈顶以下的元素。栈顶以下的元素已经不在栈中,没有意义。
这样,无论 ESP 的值如何变化,以 EBP 的值为基准能够安全访问到相关函数的局部变量、参数、返回地址,这就是 EBP 寄存器作为栈帧指针的作用。...根据上面栈帧结构和 CALL 指令的操作可知,在将属于调用函数的 EBP 的值压栈之前,ESP 指向的地址存储的是由 CALL 指令压栈的调用函数中调用位置的下一条指令的地址(原 EIP)。...和 ESP 的值均不在该线程堆栈范围之中,也就是说:要么是 TEB 中的堆栈范围被修改了,要么是当前栈帧所处的堆栈被移动到自己分配的内存里了,也就是说,栈被“截断”并“移动”了。...在 ShellCode 代码执行即将完成时,应会再将 ESP 和 EBP 的值还原回原来真正栈里的地址,避免弹栈时进入上面未知的内存区域导致程序异常。...mov [eax], ebx ; 修正调用者 ebp 在栈中位置 } // 执行正式函数体代码 simplesubfunc(); // 恢复栈指针到原栈中的位置并释放内存
一个由系统自动分配的内存空间,譬如调用函数、创建临时变量时内存空间的创建与销毁。 用于存储函数内部的局部变量、方法调用、函数传参数值等。 由高地址向低地址生长。...每调用一次函数便会创建一个独立栈帧。 栈帧中存放的是函数中的必要信息,如局部变量、函数传参、返回值等。 当函数运行完毕栈帧将会销毁。 下面进入主题,图解函数栈帧的创建与销毁过程。...在调试过程中将转到反汇编,便能直观的看到main函数栈帧创建的过程。首先需明确的是,函数栈帧由寄存器esp,ebp维护。...2.esp值传递给ebp。 3.esp减去0E4h:由于栈先使用高地址后使用低地址,减去一个值意味着esp指针向低地址移动了0E4h个地址,此处便开辟了main函数的栈帧。...,ebx,随后将ebp赋给esp并弹出ebp,最后执行ret指令返回到调用Add函数的call指令的下一地址,在执行ret指令时实际已弹出After call,以执行指令 add esp,8,此时esp
上一篇 : 栈论 : 递归与栈式访问,如何用栈实现所有递归操作(基础知识篇) 2.函数调用底层篇(了解递归调用的硬件实现) 一开始,main函数没有调用add之前他的栈帧如下图,当然,下面只是简略介绍...当要调用add函数的时候main 将 自己的变量拷贝后压入栈中,我们称之为“形参” ?...上图中变量c 和变量d的拷贝就是所谓的”形参“ 接下来将main函数的ebp地址压入栈中保存,以便add函数调用完之后恢复main在内存中的栈帧 ?...2.让esp = esp - X ; X是一个位移量,表示esp要上移,esp上移的这个位移量差不多是add函数栈帧的大小。(还有一些寄存器之类的会占用空间,忽略不计) 如图: ?...而从 ebp + 8 和 ebp + 12 读取到的正好是main函数栈帧中的形参 ? 栈帧通信总结1. 子函数直接调用父函数栈帧内的形成,访问父函数 这是子向父索求信息,那么父向子索取信息呢?
地址空间与物理内存 (1)地址空间与物理内存是两个完全不同的概念,真正的代码及数据都存在物理内存中。...栈帧状态值:保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,前栈帧的顶部可以通过栈帧平衡计算得到),用于在本栈被弹出后恢复出上一个栈帧。...(1)这里首先main函数建立自己的栈帧结构;main()函数是由__tCRTStartup()函数调用的,所以mainCRTStratup()函数调用__tmainCRTStra()函数的时候就会从栈上为...这其中也许有进栈、出栈的动作,栈指针ESP也会上下移动,但EBP是保持不变的。这意味着我们可以一直用[EBP+…]找到第一个参数,而不管在函数中有多少进出栈的动作。...紧接着当被调用者执行完毕时将消除栈帧结构,调用pop指令。 在把程序控制权返还给调用者前,被调用者foo必须先把返回值保存在EAX寄存器中。其次,foo必须恢复EBX,ESI和EDI寄存器的值。
从头开始还是从调用之后开始? (3)返回值是如何带出来的?...esp:专门用作堆栈指针,被形象的称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,esp就越来越小。在32位平台上,esp每次减少4个字节。 ebp:堆栈的栈底指针。...2.0040104E mov esp,ebp使得被调用函数栈帧回退。此时栈帧空间的内容还存在。 3.pop ebp 两个动作,出栈,并将出栈的值赋给ebp。...(3)返回值是如何带出来的? 答: (1)形参的内存空间的开辟和清理是由调用方执行的。 (2)主函数调用函数后执行执行调用之后的代码,是因为调用方在进行调用的过程中,将下一行指令的地址压栈。...所以调用完成之后是从调用之后开始,不会从头开始。 (3)返回值是由累加寄存器eax带出来的(当返回值的字节数小于等于四个自己时)。
一、概述 函数栈帧是在内存中的栈区为被调函数开辟的一块空间,里面用来存放该函数中定义的变量等东西,当函数运行完毕栈帧将被销毁。...edx "数据寄存器’,在进行乘、除法运算时,可作为默认的操作数参数参与运算 esp 栈指针寄存器,存放函数栈顶地址 ebp 帧指针寄存器,存放函数栈底地址 esp和ebp这两个寄存器中存放的是地址...a,即b指向a sub a num a的值减去num,即a向低地址移动 lea(load effective adress) 加载有效地址(在示例中理解) 四、函数栈帧的创建 所有函数的调用都会在内存里面的栈区创建函数栈帧...赋值给ebp,即将esp移动到ebp的位置 sub esp,0E4h //将esp向低地址移动0E4h个字节的位置 push ebx...首先看main函数 栈使用空间是由高地址到低地址 正在调用哪个函数,esp和ebp就维护哪个函数,在这里,我们调用的是main函数,那么就维护main函数。
, 在函数实现的位置,首先将ebp压栈,这个时候的ebp保存的是调用者的栈帧的栈底地址。...然后进行了一句pop ebp将之前存储的ebp的内容还原,这个时候ebp指向的是调用者的函数栈帧的栈底位置。...当函数调用完成后,ebp还原到调用者的栈底部,这个时候不可能再使用ebp间接寻址的方式来找到在上一个函数中定义的局部变量了,及时我们及时保存了这个变量的地址,也有可能在调用下一个函数时,这个地址所在的内存变成了下一个函数的函数栈...首先在调用这个函数时压栈方式也是从右至左一次压栈,但是函数调用完毕,返回时只要一句ret,而在main函数中多了一句add esp, 8从这个地方可以很明显的看出,最后参数所在空间的释放是由main函数释放...,也就是函数栈的释放是由调用方来完成。
说到函数我们必须要提起调用约定这个名词,而调用约定离不开栈的支持,栈在内存中是一块特殊的存储空间,遵循先进后出原则,使用push与pop指令对栈空间执行数据压入和弹出操作。...当栈顶指针esp小于栈底指针ebp时,就形成了栈帧,栈帧中可以寻址的数据有局部变量,函数返回地址,函数参数等。...不同的两次函数调用,所形成的栈帧也不相同,当由一个函数进入另一个函数时,就会针对调用的函数开辟出其所需的栈空间,形成此函数的独有栈帧,而当调用结束时,则清除掉它所使用的栈空间,关闭栈帧,该过程通俗的讲叫做栈平衡...首先先来写一段非函数版的堆栈使用案例,案例中模拟了编译器如何生成Main函数栈帧以及如何对栈帧初始化和使用的流程,笔者通过自己的理解写出了Debug版本的一段仿写代码。...函数并不会被释放所以它的栈也是稳定的,调用function函数时只需要将栈首地址通过push eax的方式传递给function函数内,并在函数内通过mov ecx,dword ptr [ ebp +
3、函数调用时参数时如何传递的?传参的顺序是怎样的? 4、函数的形参和实参分别是怎样实例化? 5、函数的返回值是如何带回的? 三、函数栈帧的创建和销毁解析 1、什么是栈?...2、这块空间的维护是使用了2个寄存器: esp 和 ebp , ebp 记录的是栈底的地址, esp 记录的是栈顶的地址。 3、函数栈帧的创建和销毁过程,在不同的编译器上实现的方法大同小异。...将求出的和放在 eax 寄存器中准备带回。 3.5、函数栈帧的销毁 当函数调用要结束返回的时候,前面创建的函数栈帧也开始销毁。 那具体是怎么销毁的呢?我们看一下反汇编代码。...1、局部变量是如何创建的? 答:一个新的函数帧栈被创建,并将局部变量推入到函数栈帧空间,然后为其分配内存空间。 2、为什么局部变量不初始化内容是随机的?...同时,初始化为确切内容时可以说成是覆盖掉了0xCC。 3、函数调用时参数是如何传递的?传参的顺序是怎样的?
正文 这篇blog试图讲明当一个c函数被调用时,一个栈帧(stack frame)是如何被建立,又如何被消除的。...让我们一步步地看一下在c函数调用过程中,一个栈帧是如何建立及消除的。 函数调用前调用者的动作 在我们的例子中,调用者是main,它准备调用函数foo。...图 2 被调用者在函数调用后的动作 当函数foo,也就是被调用者取得程序的控制权,它必须做3件事:建立它自己的栈帧,为局部变量分配空间,最后,如果需要,保存寄存器EBX,ESI和EDI的值...首先foo必须建立它自己的栈帧。EBP寄存器现在正指向main的栈帧中的某个位置,这个值必须被保留,因此,EBP进栈。然后ESP的内容赋值给了EBP。...图 4 foo的函数体现在可以执行了。这其中也许有进栈、出栈的动作,栈指针ESP也会上下移动,但EBP是保持不变的。
说到函数我们必须要提起调用约定这个名词,而调用约定离不开栈的支持,栈在内存中是一块特殊的存储空间,遵循先进后出原则,使用push与pop指令对栈空间执行数据压入和弹出操作。...当栈顶指针esp小于栈底指针ebp时,就形成了栈帧,栈帧中可以寻址的数据有局部变量,函数返回地址,函数参数等。...不同的两次函数调用,所形成的栈帧也不相同,当由一个函数进入另一个函数时,就会针对调用的函数开辟出其所需的栈空间,形成此函数的独有栈帧,而当调用结束时,则清除掉它所使用的栈空间,关闭栈帧,该过程通俗的讲叫做栈平衡...V:类Linux系统默认约定,前八个参数放入(RDI,RSI, RDX, RCX, R8, R9),剩下的参数压栈保存.首先先来写一段非函数版的堆栈使用案例,案例中模拟了编译器如何生成Main函数栈帧以及如何对栈帧初始化和使用的流程...函数并不会被释放所以它的栈也是稳定的,调用function函数时只需要将栈首地址通过push eax的方式传递给function函数内,并在函数内通过mov ecx,dword ptr [ ebp +
领取专属 10元无门槛券
手把手带您无忧上云