IA32的栈帧结构和函数调用过程
2016-11-03 00:20
381 查看
本文力图通过简单的交换函数,来解释清楚IA32的栈帧结构和函数调用过程。
C语言代码如下:void swap(int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; } int main() { int course1 = 15213; int course2 = 18213; swap(&course1, &course2); return 0; }
对应的反汇编代码如下:只列出了main和swap部分的代码
080483b4 <swap>: 80483b4: 55 push %ebp 80483b5: 89 e5 mov %esp,%ebp 80483b7: 83 ec 10 sub $0x10,%esp 80483ba: 8b 45 08 mov 0x8(%ebp),%eax 80483bd: 8b 00 mov (%eax),%eax 80483bf: 89 45 f8 mov %eax,-0x8(%ebp) 80483c2: 8b 45 0c mov 0xc(%ebp),%eax 80483c5: 8b 00 mov (%eax),%eax 80483c7: 89 45 fc mov %eax,-0x4(%ebp) 80483ca: 8b 45 08 mov 0x8(%ebp),%eax 80483cd: 8b 55 fc mov -0x4(%ebp),%edx 80483d0: 89 10 mov %edx,(%eax) 80483d2: 8b 45 0c mov 0xc(%ebp),%eax 80483d5: 8b 55 f8 mov -0x8(%ebp),%edx 80483d8: 89 10 mov %edx,(%eax) 80483da: c9 leave 80483db: c3 ret 080483dc <main>: 80483dc: 55 push %ebp 80483dd: 89 e5 mov %esp,%ebp 80483df: 83 ec 18 sub $0x18,%esp 80483e2: c7 45 f8 6d 3b 00 00 movl $0x3b6d,-0x8(%ebp) //15213 80483e9: c7 45 fc 25 47 00 00 movl $0x4725,-0x4(%ebp) //18213 80483f0: 8d 45 fc lea -0x4(%ebp),%eax 80483f3: 89 44 24 04 mov %eax,0x4(%esp) 80483f7: 8d 45 f8 lea -0x8(%ebp),%eax 80483fa: 89 04 24 mov %eax,(%esp) 80483fd: e8 b2 ff ff ff call 80483b4 <swap> 8048402: b8 00 00 00 00 mov $0x0,%eax 8048407: c9 leave 8048408: c3 ret 8048409: 90 nop 804840a: 90 nop 804840b: 90 nop 804840c: 90 nop 804840d: 90 nop 804840e: 90 nop 804840f: 90 nop
汇编代码详解过程与运行图解
函数调用前的准备工作:对应于C代码的11-13行,反汇编代码的20-29行
第21行,ebp入栈,也就是较早帧的帧底的内存地址入栈
第22行,esp的值赋值给ebp,也就是ebp和esp同时指向栈顶,此时栈顶里面存放的是较早帧的帧底的内存地址
第23行,esp的值减小24,也就是栈指针下移24个字节,此时栈顶指针的值是0x80483ec。包括栈顶,总共有6个字的空间可用来存放局部变量和实参
第24-25行,将立即数赋值给帧底下移8字节和4字节所在的内存,也就是存放局部变量
第26-27行,将局部变量course1的地址赋值给栈顶上移4字节的地址
第28-29行,将局部变量course2的地址赋值给栈顶的地址,此时栈顶指针的值仍是0x80483ec
第30行,调用swap函数,返回地址压入当前帧中,作为它里面的最后一个元素,并且将程序计数器eip里面的数值改变为被调用者的地址,此时栈顶指针的值是0x80483e8
以上过程完成之后,栈和相关寄存器的情况如下图所示:
函数调用过程(不包括返回):对应于C代码的1-7行,反汇编代码的1-18行
第2行,ebp入栈,也就是也就是较早帧的帧底的内存地址入栈,也就是0x8048404入栈,此时栈顶指针的值是0x80483e4
第3行,esp的值赋值给ebp,也就是ebp和esp同时指向栈顶,此时两个寄存器里面的值都是0x80483e4
第4行,esp的值减小16个字节,也就是栈指针下移16个字节,此时栈顶指针的值是0x80483d4。包括栈顶,总共有4个字的空间可用来存放局部变量和实参
第5-7行,将实参1所指向的内存空间的值赋值给0x80483dc
第8-10行,将实参2所指向的内存空间的值赋值给0x80483e0
第11-13,找到实参1所指向的内存空间,并将其修改为0x80483e0所在的值
第14-16,找到实参2所指向的内存空间,并将其修改为0x80483dc所在的值,至此两个数交换完成
以上过程完成之后,栈和相关寄存器的情况如下图所示:
函数返回过程:对应于反汇编代码的30、17、18行
第30行,调用函数swap,将返回地址压栈,作为当前帧的帧顶,其值是0x8048402(通过观察汇编代码得到)
第17行,leave指令,相当于两条指令:mov ebp,esp,pop ebp。注意:pop ebp产生两个效果,一是栈顶指针上移,二是ebp的被赋值为被弹出元素的值,此时ebp指向调用者帧的帧底,esp指向调用者帧的帧顶
第18行,ret指令,将返回地址弹出,并赋值给eip,此时eip的值为0x8048402,esp的值为0x08483ec,函数调用结束,main函数继续向下执行
内容参考:http://blog.csdn.net/xr_acmer/article/details/38379391
《深入理解计算机系统:第2版》第三章
相关文章推荐
- 函数的调用过程(栈帧结构)—C语言版
- 函数调用过程:EBP、ESP等栈帧的变化
- 函数的调用过程(栈帧的创建和销毁)
- 深入理解函数的调用过程——栈帧
- 栈帧与函数调用过程的分析
- 堆栈、栈帧与函数调用过程分析
- MIPS 架构上函数调用过程中的堆栈和栈帧
- c函数调用过程原理及函数栈帧分析
- 栈帧——函数的调用过程
- 函数的调用过程、栈帧的创建以及销毁
- c语言函数调用过程原理及函数栈帧分析
- 函数调用过程栈帧变化详解
- 函数的调用过程—栈帧
- c函数调用过程原理及函数栈帧分析
- 函数调用过程——栈帧
- 函数的调用过程(函数调用栈帧的创建)
- c函数调用过程原理及函数栈帧分析
- c函数调用过程原理及函数栈帧分析
- 【C】函数的调用过程,栈帧的创建和销毁
- 堆栈、栈帧与函数调用过程分析