关闭

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

1042人阅读 评论(0) 收藏 举报
首先要知道,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我们可以方便的回溯查看各个函数的详细情况。
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:84044次
    • 积分:1370
    • 等级:
    • 排名:千里之外
    • 原创:58篇
    • 转载:0篇
    • 译文:0篇
    • 评论:47条
    最新评论