子函数调用——对栈帧的理解

原创 2012年03月25日 21:06:58
首先要知道,EBP中存储的值始终指向栈底,ESP则始终指向栈顶。
汇编如下

main:

.... 

call fun
....

....

....


fun:
push ebp

mov ebp,esp

call fun1

....
leave
ret


一图胜千言,图中的是一条指令执行完后栈的状态

              初始状态                                   call  fun                                   push ebp                                 mov ebp,esp

      

                                                                                                                                                                                                                                                                          

  push 0x0012  push 0x0034                  call fun1                                push ebp                                  mov ebp,esp



接下来是函数依次返回

                 mov esp,ebp                             pop ebp                                   ret (fun1)                                pop  pop                                        


                          

                  mov esp,ebp (这里ebp的值是0xF0)                                      pop ebp                                    ret  (main)

                                                      


指令分析如下
1.call fun时,cpu将call fun的下一条指令内存地址eip+n(n视指令长度而定)入栈,之后esp-4->esp,(一个内存地址为4B)


2.push ebp, ebp内容入栈,之后esp<-(esp-4),(一个内存地址为4B)


3.mov ebp,esp, ebp<-esp,开始此函数的栈帧,这条汇编执行完之后,下条汇编之前,ebp中的值是和esp中的一样的,但是esp的值会随着后面的对栈的操作push、pop而变化。

这里的ebp就可以理解成是esp的一个拷贝。可以这么理解,ebp中存储的内存地址是此函数的栈底(不能在这个地址上存数据,要从ebp-4开始存数据)。
接下来,我们可以向这个栈中存储函数中的参数了,或者再调用子函数call fun1。push,pop.....


4.leave的作用相当于以下语句:

              mov esp,ebp
              pop ebp
a. mov esp,ebp,esp<-ebp,恢复之前拷贝的esp值,一般来说在这一句之前esp==ebp,不排除可能push和pop不对称导致esp,ebp寄存器中内容不一致。
b. pop ebp,呃,恢复之前的存储的ebp值。


5.ret 函数返回,cpu从栈中弹出一个4B的数并装入eip中,并执行。对应于步骤1,这就是最刚开始压入的下条指令地址。



总结:
从call fun 开始,栈中先后压入了下条指令地址,ebp的值,以及函数执行时压入的各个值。
函数结束时,栈中弹出的依次是函数执行时压入的值,ebp的值,下条指令地址。


push ebp
mov ebp,esp
这两句和
leave
就构成了栈框架的建立和释放。


问:为什么ebp的值显得如此重要呢?

答:
从第3步可知,ebp构成了一个函数的栈底,我们可以使用$(ebp-n)来方便的引用函数fun中的变量。
而且$(ebp)是调用此函数fun的函数的栈底,我们可以想象再fun函数中又执行了一个子函数foo,同样会建立栈帧....
通过ebp我们可以方便的回溯查看各个函数的详细情况。

堆栈-栈帧-函数调用过程分析

  • 2011年11月24日 21:51
  • 51KB
  • 下载

堆栈、栈帧与函数调用过程分析

  • 2011年11月13日 09:37
  • 363KB
  • 下载

栈帧——函数调用,变量在内存中如何存取

首先我们先要搞懂程序的地址空间是怎样的。 堆区和栈区相对生长,而我们的变量是存在栈区,接下来我们就要对程序运行时的栈区进行讨论。 用以下程序为例: #include #include int ...

函数调用原理——栈帧

函数调用原理————栈帧 1.栈 栈是向下生长的,也就是由内存高地址-》低地址的。栈有自己的栈顶指针和栈底指针。 ebp : 称为“基址指针”,在未改变之前一直指向栈底 esp : 称为“栈指针” ,...
  • D_leo
  • D_leo
  • 2016年11月18日 16:55
  • 214

函数调用入栈基本步骤(感觉和进程的栈帧结构一块看会比较容易理解)

一、函数调用的基本步骤 函数调用大致包括以下几个步骤。 (1)参数入栈:将参数从右向左依次压入系统栈中。 (2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行。...

函数调用过程:EBP、ESP等栈帧的变化

从函数调用过程看栈帧的变化

x86-64 下函数调用及栈帧原理

一蓑一笠一扁舟,一丈丝纶一寸钩。 一曲高歌一樽酒,一人独钓一江秋。 ——题秋江独钓图缘起在 C/C++ 程序中,函数调用是十分常见的操作。那么,这一操作的底层原理是...
  • lqt641
  • lqt641
  • 2017年06月10日 19:06
  • 506

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

一、栈帧 栈帧:“栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。 实际上,可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元...

堆栈、栈帧与函数调用过程分析

函数调用是程序设计中的重要环节,也是程序员应聘时常被问及的,本文就函数调用的过程进行分析。 一、堆和栈 首先要清楚的是程序对内存的使用分为以下几个区: l         栈区(stack):由...

C语言函数调用过程--栈帧

什么是栈帧? 每一次函数调用都为这次函数调用开辟一块空间,这个空间就叫做栈帧。 首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:子函数调用——对栈帧的理解
举报原因:
原因补充:

(最多只允许输入30个字)