栈结构在内存中占用一段连续存储空间,通过esp与ebp这两个栈指针寄存器来保存当前栈起始地址与结束地址,每4个字节保存一个数据。...当栈顶指针esp小于栈底指针ebp时,就形成了栈帧,栈帧中可以寻址的数据有局部变量,函数返回地址,函数参数等。...不同的两次函数调用,所形成的栈帧也不相同,当由一个函数进入另一个函数时,就会针对调用的函数开辟出其所需的栈空间,形成此函数的独有栈帧,而当调用结束时,则清除掉它所使用的栈空间,关闭栈帧,该过程通俗的讲叫做栈平衡...这种调用方式规定函数调用者在将参数压入栈中后,再将控制权转移到被调用函数,被调用函数通过栈顶指针ESP来访问这些参数。函数返回时,由调用者程序负责将堆栈平衡清除。...该调用方式在函数内不进行任何平衡参数操作,而是在退出函数后对esp执行加4操作,从而实现栈平衡。
EBP栈顶初始值为0018FF84h,ESP初始为0018FF48h ESP和EBP在栈中的作用 在每个函数最开始的地方有两条语句 push ebp mov ebp,esp 在函数返回前也有两条语句...mov esp,ebp pop ebp 每运行一个函数就新开一段栈空间,所谓的开栈空间就是移动ebp栈底,在移动ebp之前,通过push ebp保存上一级函数的栈底,然后用ebp指向现在函数栈的栈顶,即为当前函数开辟了栈...;接着给局部变量进行地址分配以及保存现场等,esp不断向低地址移动,当函数调用结束时,esp指回当前函数的栈顶(mov esp,ebp),然后上一级函数的栈顶地址出栈保存在ebp中(pop ebp)。.../ ebp保存栈顶0,ebp=esp=0018FF44h 00401023 sub esp,48h // esp -= 48h开辟了一段栈空间,...// ebp恢复为0018FF44h 004010C1 ret // 返回,等待执行函数调用的下一条指令 在调用fun函数之前,
push ebp ; 先将属于调用函数的 EBP 值压栈,执行后 ESP 指向地址存储该 EBP 的值 mov ebp, esp ; 将 ESP 的值赋给 EBP 寄存器,执行后 EBP 指向地址存储属于调用函数的...EBP 始终保持不变 mov esp, ebp ; 将 EBP 的值赋给 ESP 寄存器,执行后 ESP 指向地址存储属于调用函数的 EBP 的值 pop ebp ; 弹出保存在栈中的调用函数的...根据上面栈帧结构和 CALL 指令的操作可知,在将属于调用函数的 EBP 的值压栈之前,ESP 指向的地址存储的是由 CALL 指令压栈的调用函数中调用位置的下一条指令的地址(原 EIP)。...下面的代码片段实现了分配新的缓冲区,并拷贝从 ESP 指针指向位置到 调用函数的 EBP 在栈中存储位置加上调用函数的返回地址的存储位置这个范围的栈片段,到新分配的缓冲区中最高位置区域,为低内存预留了...delta = p_ebp - p_esp; // 获取调用者的 EBP 在栈中的位置 if (p_esp > stacklimit && p_esp < stackbase
开发环境 Ubuntu 14.04(32bits) GCC 编辑器 Cmd Markdown 画图工具 Processon 1,函数调用过程 今天先介绍下基本的函数调用过程,即栈帧。...1.1栈帧 每个函数调用都对应一个栈帧。每个栈帧由ESP和EBP寄存器来确定。每个函数执行时,其局部变量都是在自己对应的栈帧内分配内存。...//将ebp压栈,即old ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp //ebp=esp .cfi_def_cfa_register...5 andl $-16, %esp //esp=esp&0xFFFFFFF0,即保证esp的最低4位为0 subl $32, %esp //栈向低地址生长,esp-=32 movl $10...//将变量i的值压栈 call test //调用test函数,其中将下条指令(即movl %eax, 28(%esp))\ 的地址压栈,即ret addr,然后转到test函数执行,即eip=test
当一个函数被调用时,它的局部变量和函数参数等信息会被压入栈中。ESP寄存器指向栈顶的地址,即最后被压入栈的数据所在的内存地址。使用ESP寄存器,可以轻松地在栈上分配和释放内存。...总之,ESP寄存器在x86架构中用于指向当前栈顶的地址,可以方便地进行函数调用和栈操作。...每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。 这块空间的维护是使用了2个寄存器: esp 和 ebp , ebp 记录的是栈底的地址, esp 记录的是栈顶的地址。...esp,0E4h //sub会让esp中的地址减去一个16进制数字0xe4,产生新的esp,此时的esp是main函数栈帧的esp,此时结合上一条指令的ebp和当前的esp,ebp和...将main函数的 ebp 压栈 计算新的 ebp 和 esp 将 ebx , esi , edi 寄存器的值保存 计算求和,在计算求和的时候,我们是通过 ebp 中的地址进行偏移访问到了函数调用前压栈进去的参数
当栈顶指针esp小于栈底指针ebp时,就形成了栈帧,栈帧中可以寻址的数据有局部变量,函数返回地址,函数参数等。...不同的两次函数调用,所形成的栈帧也不相同,当由一个函数进入另一个函数时,就会针对调用的函数开辟出其所需的栈空间,形成此函数的独有栈帧,而当调用结束时,则清除掉它所使用的栈空间,关闭栈帧,该过程通俗的讲叫做栈平衡...; 保存栈底指针ebp mov ebp,esp ; 调整当前栈底指针到栈顶 sub esp,0e4h...这种调用方式规定函数调用者在将参数压入栈中后,再将控制权转移到被调用函数,被调用函数通过栈顶指针ESP来访问这些参数。函数返回时,由调用者程序负责将堆栈平衡清除。...该调用方式在函数内不进行任何平衡参数操作,而是在退出函数后对esp执行加4操作,从而实现栈平衡。
相关汇编命令 mov:数据转移指令 push:数据入栈,同时esp栈顶寄存器也要发生改变 pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变 sub:减法命令 add:加法命令 call:函数调用...2、这块空间的维护是使用了2个寄存器: esp 和 ebp , ebp 记录的是栈底的地址, esp 记录的是栈顶的地址。 3、函数栈帧的创建和销毁过程,在不同的编译器上实现的方法大同小异。...mov ecx,dword ptr [ebp-8] //传递a,将ebp-8处放的10放在ecx寄存器中 00BE1857 push ecx //将ecx的值压栈,esp-4 //跳转调用函数...//在栈顶弹出一个值,存放到ebx中,esp+4 00BE1782 mov esp,ebp //再将Add函数的ebp的值赋值给esp,相当于回收了Add函数的栈帧空间 00BE1784 pop ebp...//弹出栈顶的值存放到ebp,栈顶此时的值恰好就是main函数的ebp,esp+4,此时恢复了main函数的栈帧维护,esp指向main函数栈帧的栈顶,ebp指向了main函数栈帧的栈底。
所以任何函数调用进来的第一件事都是保护调用者的帧指针,以使得返回时可以恢复调用者的帧指针,即pushl %ebp movl %esp %ebp有了上面这两个命令,函数就可返回了,返回时只要leave...在函数执行过程中,栈指针esp会随着数据的入栈和出栈而移动,因此函数中对大部分数据的访问都基于帧指针%ebp进行。?对于函数A调用函数B的情况,传递给B的参数包含在A的栈帧中。...(执行完此指令后,栈指针esp会向下移动,指向保存原ebp的空间的地方)4 movl %esp,%ebp (把数据(原ebp的值)压入栈后,栈指针esp会向下移动4个字节指向放原ebp的值的空间,执行此句指令的意义就...是把esp指向的地址赋值给%ebp,%ebp作为新栈:_swap栈的栈底指针或者说是新栈的帧指针ebp使用)5 subl $4,%esp # 为局部变量c在栈内分配空间。...)的地址传送给寄存器%eax,此时 帧指针esp指向了-12%ebp处)25 pushl %eax # 作为调用的参数并压入栈中。
在系统中程序执行的时候栈都是从高地址往低地址增长的 函数参数压栈,一般从右向左压栈(比如__cdecl函数调用约定) EIP寄存器存储当前执行指令的内存位置 EBP寄存器表明当前栈帧的栈底 ESP寄存器表明当前栈帧的栈顶...push ebp 步骤2.2 修改栈底,将当前ESP设置为EBP,切换到当前函数FunAdd的栈帧。...mov ebp,esp 步骤2.3 将ESP减去8,即栈增长8个字节(记住栈是从高地址往低地址增长的)这个操作就等于在栈上申请了8个字节的空间,为什么是8个字节呢?...mov esp,ebp pop ebp 步骤2.9 此时的ESP指向的值正是在第一步中保存的Return Address,即FunAdd调用后的下一条指令。...add esp,8 mov dword ptr [ebp-0Ch],eax 首先调用add esp, 8即将栈顶去除八个字节,而这8个字节正是用来存储FunAdd入栈参数的。
2、这块空间的维护是使用了两个寄存器:esp和ebp(也可以理解成两个指针),ebp记录的是栈底的地址,esp记录的是栈顶的地址,而这两个地址就是用来维护函数栈帧的。...2.mov ebp,esp move指令会把esp的值存放带ebp中,相当于产生了main函数的ebp,这个值就是invoke_main函数栈帧的esp。...相当于回收了Add函数的栈帧空间 pop ebp 弹出栈顶的值存放到ebp,栈顶此时的值恰好就是main函数的ebp, esp+4,此时恢复了main函数的栈帧维护,esp...因为esp(栈顶寄存器)和ebp(栈底寄存器)用来维护函数的栈帧,他会根据调用函数的不同去向不同的位置,由于栈区的使用习惯时从高地址指向低地址,那么当Add函数执行完后想要回到main函数,此时Add的...ebp恰好就可以是main函数的esp,但是main函数的ebp此时已经不知道在哪里了,为了避免这种情况,创建Add函数栈帧的时候,esp和ebp在变化维护的栈帧空间之前,会记录原来空间的栈底地址也就是
esp:专门用作堆栈指针,被形象的称为栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,esp就越来越小。在32位平台上,esp每次减少4个字节。 ebp:堆栈的栈底指针。...开始分析函数栈帧开辟的过程: 00401060 push ebp 00401061 mov ebp,esp 00401063 sub esp,4Ch...2.0040104E mov esp,ebp使得被调用函数栈帧回退。此时栈帧空间的内容还存在。 3.pop ebp 两个动作,出栈,并将出栈的值赋给ebp。...esp,8 相当于esp = esp + 8,是主函数压实参的栈帧回退。...答: (1)形参的内存空间的开辟和清理是由调用方执行的。 (2)主函数调用函数后执行执行调用之后的代码,是因为调用方在进行调用的过程中,将下一行指令的地址压栈。
每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。 2.这块空间的维护是使用了2个寄存器: esp 和 ebp , ebp 记录的是栈底的地址, esp 记录的是栈顶的地址。...mov ecx,dword ptr [ebp-8] //传递a,将ebp-8处放的3放在ecx寄存器中 00BE1857 push ecx //将ecx的值压栈,esp-4 //跳转调用函数 00BE1858...ebp-8处放的3放在ecx寄存器中 00BE1857 push ecx //将ecx的值压栈,esp-4 //跳转调用函数 00BE1858 call 00BE10B4 00BE185D add esp...esp,8 00BE1860 mov dword ptr [ebp-20h],eax call 指令是要执行函数调用逻辑的,在执行call指令之前先会把call指令的下一条指令的地址进行压栈 操作,这个操作是为了解决当函数调用结束后要回到...将main函数的 ebp 压栈 计算新的 ebp 和 esp 将 ebx , esi , edi 寄存器的值保存 计算求和,在计算求和的时候,我们是通过 ebp 中的地址进行偏移访问到了函数调用前压栈进去的
(将ESP值装入EBP,更新栈帧底部) 局部变量入栈,esp减小。...函数调用图如下: ? 栈帧调整图如下: ? 函数调用时栈内的数据从高地址到低地址分别是函数参数入栈(从右到左),返回地址入栈,ebp入栈,esp分配填充地址, 局部变量mov入栈。...函数调用的汇编代码为: main函数 push ebp //保留旧栈帧 mov ebp,esp //分配新的栈帧 sub esp,4C //设置填充空间大小为4C...main函数的栈帧: ebp:0019ff40 我们看到函数调用后首先是函数参数入栈,函数调用后会发生什么呢?...我们来看一看: push ebp 保存main函数的栈帧ebp mov ebp,esp //设置funcA的栈帧ebp sub esp,44 //给fucA栈帧分配填充字节空间
我们的程序中正在调用哪个函数,ebp 和 esp维护的就是哪个函数的栈帧。...是的,ebp和esp新维护的这块空间其实就是给main函数开辟的空间,也就是main函数的栈帧 所以,我们上面也提到:我们的程序中正在调用哪个函数,ebp 和 esp维护的就是哪个函数的栈帧...总结一下: sub会让esp中的地址减去一个16进制数字0xe4,产生新的esp,此时新的esp就是main函数栈帧的esp,此时结合上一条指令的ebp和当前的esp,ebp和esp之间维护了一块新的栈空间...那我们还是来带大家简单分析一下: 首先呢又是 push ebp 把此时的ebp的值压栈 然后mov ebp,esp 之前ebp是在维护main函数的栈帧 那现在mov ebp,esp把esp...那就变成这样了 我们发现此时ebp和esp又重新维护起了main函数的栈帧,这当然没问题,因为此时Add函数已经调用结束,就要回到main函数了。
,esp寄存器总是指向栈顶,在x86平台上这个栈是从高地址向低地址增长的,我们知道每次调用一个函数都要分配一个栈帧来保存参数和局部变量,现在我们详细分析这些数据在栈空间的布局,根据gdb的输出结果图示如下...call 80483f2 要调用函数foo先要把参数准备好,第二个参数保存在esp+4指向的内存位置,第一个参数保存在esp指向的内存位置,可见参数是从右向左依次压栈的。...在每个函数的栈帧中,ebp指向栈底,而esp指向栈顶,在函数执行过程中esp随着压栈和出栈操作随时变化,而ebp是不动的,函数的参数和局部变量都是通过ebp的值加上一个偏移量来访问,例如foo函数的参数...现在esp所指向的栈顶保存着foo函数栈帧的ebp,把这个值恢复给ebp,同时esp增加4,esp的值变成0xbffff68c。 最后是ret指令,它是call指令的逆操作: 1....注意函数调用和返回过程中的这些规则: 1. 参数压栈传递,并且是从右向左依次压栈。 2. ebp总是指向当前栈帧的栈底。 3. 返回值通过eax寄存器传递。
接下来,将好好分析一下关于函数栈帧的知识点。 一、寄存器:eax,ebx,ecx,edx,ebp,esp.而本文中重点提到的是esp和ebp!...这就需要用到了esp和ebp,它们分别指向了main函数栈帧的两个位置,以便维护栈帧: (ebp和esp就算,调用了哪块函数,就去维护哪块函数的栈帧,此时进入的是main函数。) ...根据上面所述,在调用main函数之前,esp和ebp指向的是__tmainCRTStartup这个函数的栈帧,也在维护着这个栈帧。 ...esp和ebp又开始正式地维护main函数的栈帧。...在调用函数之前,就把call指令的下一条指令记住了,已经压栈了。然后调用这个函数的上一个函数的ebp,将其存进去。
栈顶和栈底都有指针,栈顶指针是esp,栈底指针是ebp,即esp指向栈顶,ebp指向栈底。...在32位平台上,ESP每次减少4字节。 其中EBP和ESP需要重点了解一下。 二、函数栈帧介绍 每个函数被调用时都会建立栈帧,在接下来的调试过程中我将会进一步解释。...,ebp //把ebp的值赋值给esp,即esp指向ebp的地址 00E913F6 pop ebp //把ebp从栈中弹出(删除) 00E913F7 ret //ADD函数调用结束返回...main函数 00E91450 add esp,8 //给esp+8,也就是让esp朝栈底方向移动8个字节(因为我们把两个存着双字变量的值的寄存器eax和ecx也压入栈了,现在函数调用结束...00E91485 pop ebp //把ebp从栈中弹出(删除) 00E91486 ret 三、小彩蛋 main函数可以调用别的函数,它自己也是可以被调用的。
首先理解一下寄存器: eax,ebx,ecx,edx,ebp,esp。画横线的这两个寄存器存放的是地址。这两个地址是用来维护函数栈帧的。 每一次函数调用,都要在栈区创立一个空间。 什么是栈?...编写代码 详细解释栈帧创立和销毁过程 如下图所示,在栈区(计算机专门的内存空间),每个函数在栈区申请一块内存空间,称为函数栈帧。在调用哪个函数,esp和ebp就跑去维护哪个函数的栈帧。...esp和ebp首先维护main函数,再调用add函数。 此时ebp,esp维护的是这样一个空间。进入main函数的第一步是push。push压栈,栈里面放的是ebp。...当push完成之后,esp指到栈顶。 我们也可以在监视中观察到:1 2 我们可以观察到esp的地址 减少了4,意味着esp往上了4位。 接着执行move,move是把esp的值给ebp。...ebp的地址存在main函数当中,就是要让随着函数调用返回之后,随着栈帧的销毁,栈顶是很容易找到的,但是栈底不容易找到。pop弹出,ebp走了。 ebp就回回去了。
3.1 main函数栈帧创建 根据VS2013编译器调试,调用堆栈,不难发现main函数的调用链条如下: 很显然main函数在被调用时,创建了栈帧。...在调试过程中将转到反汇编,便能直观的看到main函数栈帧创建的过程。首先需明确的是,函数栈帧由寄存器esp,ebp维护。...,如图所示esp指向ebp,ebp成功压入栈中。...2.esp值传递给ebp。 3.esp减去0E4h:由于栈先使用高地址后使用低地址,减去一个值意味着esp指针向低地址移动了0E4h个地址,此处便开辟了main函数的栈帧。...,ebx,随后将ebp赋给esp并弹出ebp,最后执行ret指令返回到调用Add函数的call指令的下一地址,在执行ret指令时实际已弹出After call,以执行指令 add esp,8,此时esp
栈指针寄存器,存放函数栈顶地址 ebp 帧指针寄存器,存放函数栈底地址 esp和ebp这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的 在本节中,主要了解这俩寄存器 三、汇编指令...C语言所对应的汇编代码 int main() { push ebp //将ebp压入栈中 mov ebp,esp //将esp...首先看main函数 栈使用空间是由高地址到低地址 正在调用哪个函数,esp和ebp就维护哪个函数,在这里,我们调用的是main函数,那么就维护main函数。...通过 __tmainCRTStartup 函数调用main函数,所以要创建好__tmainCRTStartup 的栈帧 push ebp push ebp就是把__mainCRTStartup 函数栈底的地址压栈...指令,代表出栈 将edi,esi和ebx弹出栈 add esp,8这条指令,该指令的执行结果是让esp指针指向的地址加8 mov dword ptr [ebp-20h],eax指令。
领取专属 10元无门槛券
手把手带您无忧上云