返回   cpper编程论坛 > Blog > zweily
注册账号 论坛帮助 会员列表 日历事件 搜索 今日新帖 标记版面已读

为这篇文章评分

Stack

发表于 2006-01-22 07:40 AM 作者: zweily
注:这篇是笔记,由于原先贴的地方贴图不方便,于是就简单地写了几句,先搬过来,改天我会补充完整的。

IA-32的stack是由高地址向低地址延伸的,所以PUSH指令会将栈顶指针递减,POP指令将栈顶指针递增。
有2个寄存器与stack相关,分别为EBP和ESP。
EBP指向当前stack frame的栈底位置,按照IA-32的Manual上的称法,EBP是“Pointer to data on the stack”,其实应该是一个Base Pointer。由它决定了当前栈的起始位置。
ESP指向当前的栈顶,准确地说是指向当前栈里最顶端的那个数据。
PUSH指令会先将ESP递增(根据栈是2字节对齐还是4字节对齐,相应地加2或者加4),然后将要PUSH的数据放入栈中。POP指令执行相反的操作。
按照IA-32的Manual上对于过程调用(以call/ret方式实现)的说明,基本过程如下:

call
1. 将EIP(即过程返回地址)PUSH到stack上;
2. 将ESP的值存入EBP,即切换到新的stack;
3. 跳转到相应的地址执行被调用过程的指令;

ret
1. 将EBP的值存入ESP;
2. 将当前栈顶(即EBP所指位置)POP到EIP中;
3. 执行EIP地址的指令,也就是完成return;

从这个过程里,可以看到EIP的保存和切换到新stack的过程。但是这里有个问题,就是ret的时候,如何恢复原先的EBP呢?这个问题在IA-32的Manual上没有找到答案,那么就通过实际的程序来试验一下吧。
通过查看程序的汇编代码,发现新过程一开始总会执行下面的指令:

push ebp
mov ebp, esp

对应的过程结束部分有下面的指令:

mov esp, ebp
pop ebp
ret

这就对了嘛。而且也发现实际栈的EBP并不是像IA-32的Manual上说的那样指向保存的返回地址的,而是指向返回地址的下一项,也就是这里代码所显示的这样。

ok,过程调用的stack情况了解了,那么接下去看看关于参数传递的问题吧。
对于参数传递,可以有多种方式,比如通过通用寄存器来传递,或者用栈来传递。这里就讨论栈传递的方式。为了便于比较,这里就讨论c/c++ call convention和std call convention两种情况。
这里我就不讨论这两者的一些背景知识了,将注意力focus汇编指令如何实现这两种不同的方式。
前者是由调用者来负责参数的清理,而后者是由被调者来清理栈上的参数。

IA-32的RET指令可以带上一个参数,例如:
ret n ;ret的时候,将ESP + n
这个参数的作用就是为了给被调者用来清理堆栈上的参数的。

而对于c/c++ call的情况,被调者就ret,而调用者会在ret回来的地方执行下面指令:
add esp, n
这条指令就将栈顶指针指到正确的位置(即跳过了那些参数)。

那么,最后说一句为什么会需要调用者清理的这种情况呢?其实这种方式是为了对应于c中的可变长参数列表的情况,这时候只有调用者知道有多少参数,而被调者编译时不知道有多少参数传递进来了。
评论 0 Email文章
评论总数 0

评论

发表评论 发表评论
作者为 zweily 的最新文章

所有时间均为格林尼治时间 +9。现在的时间是 12:14 PM


Powered by vBulletin® 版本 3.7.0
版权所有 ©2000 - 2008,Jelsoft Enterprises Ltd.
(C) Copy Right All Right Reserved 2001 - 2007

Search Engine Friendly URLs by vBSEO 3.1.0