1、首先我们来弄清楚栈是什么?
栈:简单来说,栈就是一个先进后出的数据结构,这个和函数的调用过程一样,调用时,调用函数在前,被调用函数在后,返回时,被调用函数先返回,调用函数在后。
正好符合先进后出的结构。我们先来看看栈的push,和pop操作。比如往栈中压入一个元素,如下图:(左边为压入之前的图,压入之后则为右边的部分)
那如果弹出来呢
这样我们可以清楚看到栈的Push和pop操作。
2、栈帧是什么呢?
其实栈帧本质上也是一种栈,它专门保存函数掉用的各种信息比如变量,返回地址,参数等等。
如下图:
从上图我们可以看出:
1、栈的地址是向下生长的,也就是说栈低的地址比栈顶的高。
2、 一般来说,我们将%ebp到%esp这块区域叫做栈帧,每次掉用函数都会产生一个新的栈帧。
我们分两步来讲:
1、在被调用函数取得控制权之前的情况:
在被调用函数取得控制权之前,调用函数做了2件事情:1、将需要传入被调用函数的实际参数压入栈中,参数按照从右往左的方向依次压入。2、将返回地址压入栈中。
然后main函数用call指令调用子函数,EIP指令寄存器的内容被压入栈中,因为EIP寄存器是指向main中的下一条指令,所以现在返回地址就在栈顶了。在call指令执行完之后,下一个执行周期将从名为foo的标记处开始,进入了被调函数。
2、进入被调用函数的情况。
被调用函数做了几件事:
1、将调用函数的ebp压入栈(便于调用完成后找到调用函数的继续运行的位置),此时%esp指向它。
2、将esp赋值给ebp,这是ebp就有了新的值。它就是被调用函数的栈底。
3、此时就可以开辟新的空间了用move或者sub就可以了。
3、函数的返回。
当函数调用完毕的时候呢,将esp移到ebp处,然后将栈内的旧的ebp的值取出来放到ebp中,则就恢复到函数调用前的状态了。