函数调用栈理解
来源:互联网 发布:游戏视频制作软件 编辑:程序博客网 时间:2024/06/05 14:47
一、相关寄存器
(1) esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2) ebp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。(ebp在当前栈帧内位置固定,故函数中对大部分数据的访问都基于ebp进行)
(3) eip:指令寄存器(extended instruction pointer),其内存放着一个指针,该指针永远指向下一条等待执行的指令地址。 可以说如果控制了EIP寄存器的内容,就控制了进程——我们让eip指向哪里,CPU就会去执行哪里的指令。eip可被jmp、call和ret等指令隐含地改变(事实上它一直都在改变)(ret指令就是把当前栈顶保存的返回值地址 弹到eip中)
(4)eax:累加(Accumulator)寄存器,常用于函数返回值。
二、当函数调用的时候发生了什么?
例如:
int main(void){ foo(1,2,3) ; return 0 ;}
当方法main需要调用foo时,它的标准行为:
- 1、在main方法的调用栈中,将 foo的参数从右向左依次push到栈中。
- 2、把main方法当前指令的下一条指令地址 (即return address)push到栈中。(隐藏在call指令中)
- 3、使用call指令调用目标函数体foo。
请注意,以上3步都处于main的调用栈,其中ebp保存其栈底,而esp保存其栈顶。
接下来,在foo函数中:
- 1、push ebp: 将ebp的当前值push到栈中,即保存ebp。
- 2、mov ebp,esp: 将esp的值赋给ebp,则意味着进入了foo方法的调用栈。
- 3、[可选]sub esp, XXX: 在栈上分配XXX字节的临时空间。(抬高栈顶)(编译器根据函数中的局部变量的总大小确定临时空间的大小)
- 4、[可选]push XXX: 保存(push)一些寄存器的值。
而在foo方法调用完毕后,便执行前面阶段的逆操作:
- 1、保存返回值: 通常将函数的返回值保存在寄存器eax中。
- 2、[可选]恢复(pop)一些寄存器的值。
- 3、mov esp,ebp: 恢复esp同时回收局部变量空间。(恢复原栈顶)
- 4、pop ebp: 将栈顶的值赋给ebp,即恢复main调用栈的栈底。(恢复原栈底)
- 5、ret: 从栈顶获得之前保留的return address,并跳转到此位置继续执行。
main方法先将foo方法所需的参数压入栈中,然后再改变ebp,进入foo方法的调用栈。因此,如果在foo方法中需要访问那些参数,则需要根据当前ebp中的值,再向高地址偏移后进行访问——因为高地址才是main方法的调用栈。也就是说,地址ebp + 8存放了foo方法的第1个参数,地址ebp + 12存放了foo方法的第2个参数,以此类推。那么地址ebp + 4存放了什么呢?它存放的是return address,即foo方法返回后,需要继续执行下去的main方法指令的地址。
- 函数调用栈理解
- 多线程调用函数理解
- C++函数调用原理理解
- C++函数调用原理理解
- C++函数调用原理理解
- C++函数调用原理理解
- C++函数调用原理理解
- C++函数调用原理理解
- 函数调用堆栈的理解
- 理解立即调用函数(function(){ ...})();
- 【基础】函数运行栈与调用栈的理解
- 底层理解函数调用实现过程 栈结构 栈过程
- 理解函数调用实现过程 栈结构 栈过程
- makefile 函数调用理解与自定义函数
- 深入理解递归函数的调用过程
- 深入理解递归函数的调用过程
- 深入理解:立即调用的函数表达式
- 理解javascript函数调用和“this”
- 调试托管程序中加载的本机dll
- CSUOJ 1885 条条大路通罗马(最长路径)
- 5句经典
- SVN服务器搭建与使用二
- 线程池常用类学习笔记
- 函数调用栈理解
- pthread多线程同步大全
- scrapy 爬课程信息
- 重学java「二」
- React实战-未来是属于React的
- 字符串核函数的快速计算(一) 翻译
- mysql常用命令
- 远程连接Linux IP
- AUTOCAD二次开发-----删除一个图层里面的所有对象