您的位置:首页 > 理论基础

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版》第三章
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息