图解函数调用过程(函数栈帧)
补充知识
- ebp(base pointer),esp(stack pointer)是存放当前正在被执行的函数的栈底和栈顶指针的寄存器。
- 汇编代码中,通常用函数的名字作为标号(标号的位置是转移指令的目的地,标号作为转移的锚点)。
- push x指令:先将esp -"1"(1代表的不是数字1,而是一个最小单元),然后把x的值复制到esp指向的内存块中。
- pop x 指令:先将esp指向的内存块的内容赋值给x,然后esp+"1"。
- 访问函数栈中的数据有两种方法,一种是push,pop,还有一种就是直接用ebp,esp加减常数访问函数栈内的地址
- X86处理器中PC(program pointer)也叫IP(instruction pointer)。
研究对象
假设main函数调用caller,caller调用add,函数栈每一块内存块假设是4字节。以下是caller和add函数的c语言代码以及汇编代码,以及函数调用栈的开始状态。
研究过程
将main函数的栈底指针存放在caller函数的栈底
把temp1,temp2放到函数栈,并提前预留出存放结果的sum变量的位置。
为add函数的调用准备参数,参数的存放在调用者栈顶的部分。(下图中所说的PC是要说明,实际上,每个调用者都会在自己的函数栈存放调用前一刻的PC指针,这样调用者可以在调用结束后知道从哪儿继续执行剩下的代码)
存放PC指针,修改PC指针
同样的,add函数也要存放caller函数的栈底指针,就像caller函数的在栈底存放main函数的栈底指针一样
执行add函数,并获取返回值
下图move esp,ebp中esp和ebp代表的是寄存器,不是寄存器里面的指针所指向的函数栈里的内存块。这个命令就是让esp和ebp都指向栈底。
pop指令执行后正好回到了调用者的函数栈
将caller函数栈顶的PC指针弹出到PC寄存器里,这样,计算机就会继续执行add函数调用下面的代码。
从eax寄存器中获取返回值,并放入提前预留好的sum的位置上
用寄存器保存caller的返回值,销毁caller函数栈,回到main函数栈。