发现文字描述还是太没有快感。上几幅图,来说明这个调试过程更好。此文对于深刻理解ebp,esp是具有长远意义的
可以看到,初始情况下,ebp此时值为0012FEDC,也就是栈帧的地址,而栈顶地址esp值为0012FDFC。可以看到两个值有一定的关系。而帧指针的地址较高。
然后我们让它执行前两句,push ebp,mov ebp,esp
可以看到前两句已经执行了,那么ebp跟esp的值也发生了变化。esp=0012FDF8,ebp=0012FDF8。为神马?一句句解读,push ebp,向栈里面压入了一个东西,那么栈顶此时应该发生变化了,也就是地址-4字节。为什吗是减法呢?因为是向低地址增长的,这点一定得注意。所以此时esp变化成了0012FDFC-4=OO12FDF8.至于ebp也等于0012FDF8就不解释了
接着上图不解释:
此时呢,观察现在的值。栈顶esp=0012FDF4,而ebp=0012FDF8;没啥好说的,此时的栈顶已经又跑上去了,说明又有元素压栈了。那么执行这句mov
esp,ebp之后,不用说,esp跟ebp都会变成0012FDF8.我们重点看下一幅,执行完pop,让ebp出栈,后会发生神马。
此时ebp已经出栈了,来看看那他们的值,esp=0012FDFC,ebp=0012FEDC.首先,ebp出栈了,这个时候栈空了,所以栈顶会变成初始时的值001212FDFC。相当于上图中的esp=0012FDF8+4=0012FDFC.注意出栈,则栈顶+4,然后呢。ebp为啥变成了0012FEDC初始的值?ebp不是一直保存着esp的初始地址么?
所以重点就在pop这个语句了。pop ebp究竟表达神马意思?ebp的值起初存在了栈中,出栈以后,它的值就恢复了原样。所一句灰常重要啊。pop的意思也许就是把弹出的值赋给我们的变量,pop ebp,也就是把存在栈中的值弹出来赋给ebp。
所以我们在啰唆几句:
1、两句的mov ebp,esp实际上是把ebp进栈后的栈顶地址给了ebp。
2、在ebp没有出栈钱,它会一直保存ebp进栈以后的栈顶值,也就是1的值。
3、在ebp出栈前,需要把esp恢复到只有ebp在栈中时的值。
4、出栈后,esp自然恢复到ebp进栈以前的初始值,而pop ebp则恢复了ebp的初始值。
5、pop的语义很重要,pop ebp的意思是把当前栈顶的元素出栈,送入ebp中,而不是让ebp出栈,这点必须明确!
说完这个以后,我也有了疑问,因为从上面的解释来看,似乎栈里面的东西没有清空?就直接把esp恢复到了ebp的值,这是为神马呢?发现这就是所谓的栈平衡问题,先不纠结了,只要有前两句push ebp, mov ebp,esp跟最后两句mov esp,ebp,pop ebp就够了,这样肯定不会有问题。
搞点学习笔记,比较下cmp跟test的指令,这两个都是比较检测指令。
cmp eax,xxxx,意思是让两者相减,但是并不保存运算的结果,只用来影响相应的标志位,从而判断是否执行跳转,比如说一下的例子是很常见的:
cmp eax,1
JE xxxx
JE的跳转条件是:ZF=0,也就是当两者相等的时候,执行跳转。那么这个命令组合起来就是表示:如果eax=1,执行跳转。
顺便 再说下JNE,JNE的意思是jump if not eaqual,也就是跟JE的判断条件相反。
然后说下TEST吧,test eax,xxx:意思是让eax跟xxx执行与运算,同样也是不保存运算结果,而影响相应的标志位,TEST呢经常用来干下面这件事情.
test eax,eax
JZ xxxx
意思很明显,就是检测EAX的值是不是0,所以这个语句就是检测判断语句。JZ嘛就是jump if zero。组合起来也就是表示如果eax为0,执行跳转到xxxx。
觉得还是CALL指令最难理解,它先把IP压栈,然后跳到子程序里面去执行东西,最后IP出栈,指令返回到先前执行的地方。继续OD吧。
__________________________________________________________________________________________________________
经过了激励的思想斗争,我终于想清楚了这个过程。现在可以毫无压力的,淡定的说,ebp保存的是栈帧起始的其实地址,也就是一个函数属于自己的内存那一块的最高地址,而esp就保存了当前的这一块地址中已经使用的最低地址,说起来可能有些拗口。但是这对于深刻理解这个过程是至关重要的。
有一句话让我想明白了这个道理,ebp总是跟旧的栈帧的顶部相邻。这样我们就可以理解为什么,调用一个函数的时候需要push ebp,那就保存旧的栈帧,同时也给出了我们得到旧的ebp的方法,一旦当前的栈帧使用完毕以后,这个最后弹出来的ebp就指明了我们怎么样回到旧的函数的栈帧,或者知道那个调用我们的上一层函数的栈帧。mov ebp,esp这句话很有迷惑性。其实很好理解,这个告诉了我们新的栈帧是怎么创建的,其实就是直接的拿当前函数(ebp已经入栈后)的栈顶当做新的栈帧头部,因为此时已经有esp=ebp了,而旧的ebp被我们保存在了栈里面。
关于这个布局,CSAPP说的很清楚,深刻的理解了以上两句的作用,很多事情都能顺理成章的想通了。
其实说白了,ebp只有一个作用,就是用来暂时保存当前函数栈的起始地址,因为旧的栈帧跟新的栈帧是相邻的,ebp相当于起一个咋暗示的temp的作用。