函数调用时栈帧分析

​
本文实现环境 ubuntu12.04 gcc4.4.5

测试程序采用《专业嵌入式软件开发-李云》书中的示例源码test.c如下:

#include <stdio.h>

void tail(int _param)

{

   int local = 0;

   int reg_esp,reg_ebp;

  

   asm volatile(

      "movl %%ebp,%0 \n"

      "movl %%esp,%1 \n"

      : "=r" (reg_ebp), "=r" (reg_esp)

   );      

  

   printf("tail (): EBP = %x\n",reg_ebp);

   printf("tail (): ESP = %x\n",reg_esp);

   printf("tail (): (EBP) = %x\n",*(int *)reg_ebp);

   printf("tail (): rerurn address = %x\n",*(((int *)reg_ebp + 1)));

   printf("tail (): &local = %p\n",&local);

   printf("tail (): &reg_esp = %p\n",&reg_esp);

   printf("tail (): &reg_ebp = %p\n",&reg_ebp);

   printf("tail (): &_param = %p\n",&_param);

}

int middle(int _p0, int _p1, int _p2)

{

   int reg_esp,reg_ebp;

  

   asm volatile(

      "movl %%ebp,%0 \n"

      "movl %%esp,%1 \n"

      : "=r" (reg_ebp), "=r" (reg_esp)

   );

             

   tail(_p0);

   printf("middle (): EBP = %x\n",reg_ebp);

   printf("middle (): ESP = %x\n",reg_esp);

   printf("middle (): (EBP) = %x\n",*(int *)reg_ebp);

   printf("middle (): rerurn address = %x\n",*(((int *)reg_ebp + 1)));

   printf("middle (): &reg_esp = %p\n",&reg_esp);

   printf("middle (): &reg_ebp = %p\n",&reg_ebp);

   printf("middle (): &_p0 = %p\n",&_p0);

   printf("middle (): &_p1 = %p\n",&_p1);

   printf("middle (): &_p2 = %p\n",&_p2);

   return  1;

}

int main()

{

   int reg_esp,reg_ebp;

  

   int local = middle(1,2,3);

   asm volatile(

      "movl %%ebp,%0 \n"

      "movl %%esp,%1 \n"

      : "=r" (reg_ebp), "=r" (reg_esp)

   );

             

   printf("main (): EBP = %x\n",reg_ebp);

   printf("main (): ESP = %x\n",reg_esp);

   printf("main (): (EBP) = %x\n",*(int *)reg_ebp);

   printf("main (): rerurn address = %x\n",*(((int *)reg_ebp + 1)));

   printf("main (): &reg_esp = %p\n",&reg_esp);

   printf("main (): &reg_ebp = %p\n",&reg_ebp);

   printf("main (): &local = %p\n",&local);

   return 0;

}

在文件所在目录下输入:gcc –o stackframe.exe test.c 编译,./stackframe.exe运行结果(不同机器可能不同):

tail (): EBP = bf8d4ea8

tail (): ESP = bf8d4e80

tail (): (EBP) = bf8d4ed8

tail (): rerurn address = 804849f

tail (): &local = 0xbf8d4e9c

tail (): &reg_esp = 0xbf8d4e98

tail (): &reg_ebp = 0xbf8d4e94

tail (): &_param = 0xbf8d4eb0

middle (): EBP = bf8d4ed8

middle (): ESP = bf8d4eb0

middle (): (EBP) = bf8d4f08

middle (): rerurn address = 8048586

middle (): &reg_esp = 0xbf8d4ecc

middle (): &reg_ebp = 0xbf8d4ec8

middle (): &_p0 = 0xbf8d4ee0

middle (): &_p1 = 0xbf8d4ee4

middle (): &_p2 = 0xbf8d4ee8

main (): EBP = bf8d4f08

main (): ESP = bf8d4ee0

main (): (EBP) = bf8d4f88

main (): rerurn address = 83bce7

main (): &reg_esp = 0xbf8d4efc

main (): &reg_ebp = 0xbf8d4ef8

main (): &local = 0xbf8d4ef4

通过以上打印出来的地址可以绘制出以下函数通用时的栈帧图:



上图左侧是内存地址,这幅图虽然简单但是却反映了函数调用时发生的所有事情。

main函数调用时堆栈变化:

1、返回地址入栈

2、上个函数(原函数)的ebp入栈

3、开辟main函数栈帧

4、mian函数局部变量入栈

5、调用middle函数的3个参数(p0、p1、p2)入栈

middle函数调用时堆栈变化:

1、返回地址入栈

2、上个函数(这里就是main函数)的ebp地址入栈

3、开辟middle函数栈帧

4、middle函数局部变量入栈

5、调用tail函数的1个参数(_param)入栈

tail函数一样就不说了

其他函数调用堆栈变化都是这样的过程,这里有几点还需要解释下。

函数栈帧是如何开辟的。我们需要查看一下原程序的汇编代码。用objdump –d stackframe.exe命令可以查看执行文件的汇编代码, gcc 的as汇编器的汇编代码和x86汇编器代码语法不同,具体信息请查阅相关资料。

由于文件很大,这里我只截取3个函数的汇编代码:

080483c4 <tail>:

 80483c4:  55                        push   %ebp

 80483c5:  89 e5                      mov    %esp,%ebp

 80483c7:  83 ec 28                    sub    $0x28,%esp

 80483ca:  c7 45 f4 00 00 00 00        movl   $0x0,-0xc(%ebp)

 80483d1:  89 ea                      mov    %ebp,%edx

 80483d3:  89 e0                      mov    %esp,%eax

 80483d5:  89 55 ec                    mov    %edx,-0x14(%ebp)

 80483d8:  89 45 f0                    mov    %eax,-0x10(%ebp)

 80483db:  8b 55 ec                    mov    -0x14(%ebp),%edx

 80483de:  b8 00 87 04 08                mov    $0x8048700,%eax

 80483e3:  89 54 24 04                  mov    %edx,0x4(%esp)

 80483e7:  89 04 24                    mov    %eax,(%esp)

 80483ea:  e8 05 ff ff ff          call   80482f4 <printf@plt>

 80483ef:  8b 55 f0                    mov    -0x10(%ebp),%edx

 80483f2:  b8 13 87 04 08                mov    $0x8048713,%eax

 80483f7:  89 54 24 04                  mov    %edx,0x4(%esp)

 80483fb:  89 04 24                    mov    %eax,(%esp)

 80483fe:  e8 f1 fe ff ff          call   80482f4 <printf@plt>

 8048403:  8b 45 ec                    mov    -0x14(%ebp),%eax

 8048406:  8b 10                      mov    (%eax),%edx

 8048408:  b8 26 87 04 08                mov    $0x8048726,%eax

 804840d:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048411:  89 04 24                    mov    %eax,(%esp)

 8048414:  e8 db fe ff ff          call   80482f4 <printf@plt>

 8048419:  8b 45 ec                    mov    -0x14(%ebp),%eax

 804841c:  83 c0 04                    add    $0x4,%eax

 804841f:  8b 10                      mov    (%eax),%edx

 8048421:  b8 3b 87 04 08                mov    $0x804873b,%eax

 8048426:  89 54 24 04                  mov    %edx,0x4(%esp)

 804842a:  89 04 24                    mov    %eax,(%esp)

 804842d:  e8 c2 fe ff ff          call   80482f4 <printf@plt>

 8048432:  b8 59 87 04 08                mov    $0x8048759,%eax

 8048437:  8d 55 f4                    lea    -0xc(%ebp),%edx

 804843a:  89 54 24 04                  mov    %edx,0x4(%esp)

 804843e:  89 04 24                    mov    %eax,(%esp)

 8048441:  e8 ae fe ff ff          call   80482f4 <printf@plt>

 8048446:  b8 6f 87 04 08                mov    $0x804876f,%eax

 804844b:  8d 55 f0                    lea    -0x10(%ebp),%edx

 804844e:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048452:  89 04 24                    mov    %eax,(%esp)

 8048455:  e8 9a fe ff ff          call   80482f4 <printf@plt>

 804845a:  b8 87 87 04 08                mov    $0x8048787,%eax

 804845f:  8d 55 ec                    lea    -0x14(%ebp),%edx

 8048462:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048466:  89 04 24                    mov    %eax,(%esp)

 8048469:  e8 86 fe ff ff          call   80482f4 <printf@plt>

 804846e:  b8 9f 87 04 08                mov    $0x804879f,%eax

 8048473:  8d 55 08                    lea    0x8(%ebp),%edx

 8048476:  89 54 24 04                  mov    %edx,0x4(%esp)

 804847a:  89 04 24                    mov    %eax,(%esp)

 804847d:  e8 72 fe ff ff          call   80482f4 <printf@plt>

 8048482:  c9                        leave 

 8048483:  c3                        ret   

08048484 <middle>:

 8048484:  55                        push   %ebp

 8048485:  89 e5                      mov    %esp,%ebp

 8048487:  83 ec 28                    sub    $0x28,%esp

 804848a:  89 ea                      mov    %ebp,%edx

 804848c:  89 e0                      mov    %esp,%eax

 804848e:  89 55 f0                    mov    %edx,-0x10(%ebp)

 8048491:  89 45 f4                    mov    %eax,-0xc(%ebp)

 8048494:  8b 45 08                    mov    0x8(%ebp),%eax

 8048497:  89 04 24                    mov    %eax,(%esp)

 804849a:  e8 25 ff ff ff          call   80483c4 <tail>

 804849f:  8b 55 f0                    mov    -0x10(%ebp),%edx

 80484a2:  b8 b6 87 04 08                mov    $0x80487b6,%eax

 80484a7:  89 54 24 04                  mov    %edx,0x4(%esp)

 80484ab:  89 04 24                    mov    %eax,(%esp)

 80484ae:  e8 41 fe ff ff          call   80482f4 <printf@plt>

 80484b3:  8b 55 f4                    mov    -0xc(%ebp),%edx

 80484b6:  b8 cb 87 04 08                mov    $0x80487cb,%eax

 80484bb:  89 54 24 04                  mov    %edx,0x4(%esp)

 80484bf:  89 04 24                    mov    %eax,(%esp)

 80484c2:  e8 2d fe ff ff          call   80482f4 <printf@plt>

 80484c7:  8b 45 f0                    mov    -0x10(%ebp),%eax

 80484ca:  8b 10                      mov    (%eax),%edx

 80484cc:  b8 e0 87 04 08                mov    $0x80487e0,%eax

 80484d1:  89 54 24 04                  mov    %edx,0x4(%esp)

 80484d5:  89 04 24                    mov    %eax,(%esp)

 80484d8:  e8 17 fe ff ff          call   80482f4 <printf@plt>

 80484dd:  8b 45 f0                    mov    -0x10(%ebp),%eax

 80484e0:  83 c0 04                    add    $0x4,%eax

 80484e3:  8b 10                      mov    (%eax),%edx

 80484e5:  b8 f8 87 04 08                mov    $0x80487f8,%eax

 80484ea:  89 54 24 04                  mov    %edx,0x4(%esp)

 80484ee:  89 04 24                    mov    %eax,(%esp)

 80484f1:  e8 fe fd ff ff          call   80482f4 <printf@plt>

 80484f6:  b8 18 88 04 08                mov    $0x8048818,%eax

 80484fb:  8d 55 f4                    lea    -0xc(%ebp),%edx

 80484fe:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048502:  89 04 24                    mov    %eax,(%esp)

 8048505:  e8 ea fd ff ff          call   80482f4 <printf@plt>

 804850a:  b8 32 88 04 08                mov    $0x8048832,%eax

 804850f:  8d 55 f0                    lea    -0x10(%ebp),%edx

 8048512:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048516:  89 04 24                    mov    %eax,(%esp)

 8048519:  e8 d6 fd ff ff          call   80482f4 <printf@plt>

 804851e:  b8 4c 88 04 08                mov    $0x804884c,%eax

 8048523:  8d 55 08                    lea    0x8(%ebp),%edx

 8048526:  89 54 24 04                  mov    %edx,0x4(%esp)

 804852a:  89 04 24                    mov    %eax,(%esp)

 804852d:  e8 c2 fd ff ff          call   80482f4 <printf@plt>

 8048532:  b8 62 88 04 08                mov    $0x8048862,%eax

 8048537:  8d 55 0c                    lea    0xc(%ebp),%edx

 804853a:  89 54 24 04                  mov    %edx,0x4(%esp)

 804853e:  89 04 24                    mov    %eax,(%esp)

 8048541:  e8 ae fd ff ff          call   80482f4 <printf@plt>

 8048546:  b8 78 88 04 08                mov    $0x8048878,%eax

 804854b:  8d 55 10                    lea    0x10(%ebp),%edx

 804854e:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048552:  89 04 24                    mov    %eax,(%esp)

 8048555:  e8 9a fd ff ff          call   80482f4 <printf@plt>

 804855a:  8b 45 ec                    mov    -0x14(%ebp),%eax

 804855d:  c9                        leave 

 804855e:  c3                        ret   

0804855f <main>:

 804855f:  55                        push   %ebp

 8048560:  89 e5                      mov    %esp,%ebp

 8048562:  83 e4 f0                    and    $0xfffffff0,%esp

 8048565:  83 ec 20                    sub    $0x20,%esp

 8048568:  c7 44 24 08 03 00 00   movl   $0x3,0x8(%esp)

 804856f:  00

 8048570:  c7 44 24 04 02 00 00   movl   $0x2,0x4(%esp)

 8048577:  00

 8048578:  c7 04 24 01 00 00 00   movl   $0x1,(%esp)

 804857f:  e8 00 ff ff ff          call   8048484 <middle>

 8048584:  89 44 24 14                  mov    %eax,0x14(%esp)

 8048588:  89 ea                      mov    %ebp,%edx

 804858a:  89 e0                      mov    %esp,%eax

 804858c:  89 54 24 18                  mov    %edx,0x18(%esp)

 8048590:  89 44 24 1c                  mov    %eax,0x1c(%esp)

 8048594:  8b 54 24 18                  mov    0x18(%esp),%edx

 8048598:  b8 8e 88 04 08                mov    $0x804888e,%eax

 804859d:  89 54 24 04                  mov    %edx,0x4(%esp)

 80485a1:  89 04 24                    mov    %eax,(%esp)

 80485a4:  e8 4b fd ff ff          call   80482f4 <printf@plt>

 80485a9:  8b 54 24 1c                  mov    0x1c(%esp),%edx

 80485ad:  b8 a1 88 04 08                mov    $0x80488a1,%eax

 80485b2:  89 54 24 04                  mov    %edx,0x4(%esp)

 80485b6:  89 04 24                    mov    %eax,(%esp)

 80485b9:  e8 36 fd ff ff          call   80482f4 <printf@plt>

 80485be:  8b 44 24 18                  mov    0x18(%esp),%eax

 80485c2:  8b 10                      mov    (%eax),%edx

 80485c4:  b8 b4 88 04 08                mov    $0x80488b4,%eax

 80485c9:  89 54 24 04                  mov    %edx,0x4(%esp)

 80485cd:  89 04 24                    mov    %eax,(%esp)

 80485d0:  e8 1f fd ff ff          call   80482f4 <printf@plt>

 80485d5:  8b 44 24 18                  mov    0x18(%esp),%eax

 80485d9:  83 c0 04                    add    $0x4,%eax

 80485dc:  8b 10                      mov    (%eax),%edx

 80485de:  b8 c9 88 04 08                mov    $0x80488c9,%eax

 80485e3:  89 54 24 04                  mov    %edx,0x4(%esp)

 80485e7:  89 04 24                    mov    %eax,(%esp)

 80485ea:  e8 05 fd ff ff          call   80482f4 <printf@plt>

 80485ef:  b8 e7 88 04 08                mov    $0x80488e7,%eax

 80485f4:  8d 54 24 1c                  lea    0x1c(%esp),%edx

 80485f8:  89 54 24 04                  mov    %edx,0x4(%esp)

 80485fc:   89 04 24                    mov    %eax,(%esp)

 80485ff:   e8 f0 fc ff ff           call   80482f4 <printf@plt>

 8048604:  b8 ff 88 04 08       mov    $0x80488ff,%eax

 8048609:  8d 54 24 18                  lea    0x18(%esp),%edx

 804860d:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048611:  89 04 24                    mov    %eax,(%esp)

 8048614:  e8 db fc ff ff          call   80482f4 <printf@plt>

 8048619:  b8 17 89 04 08                mov    $0x8048917,%eax

 804861e:  8d 54 24 14                  lea    0x14(%esp),%edx

 8048622:  89 54 24 04                  mov    %edx,0x4(%esp)

 8048626:  89 04 24                    mov    %eax,(%esp)

 8048629:  e8 c6 fc ff ff          call   80482f4 <printf@plt>

 804862e:  b8 00 00 00 00                mov    $0x0,%eax

 8048633:  c9                        leave 

 8048634:  c3                        ret   

 8048635:  90                        nop

 8048636:  90                        nop

 8048637:  90                        nop

 8048638:  90                        nop

 8048639:  90                        nop

 804863a:  90                        nop

 804863b:  90                        nop

 804863c:  90                        nop

 804863d:  90                        nop

 804863e:  90                        nop

 804863f:  90                        nop

这里我们分析下main函数的栈帧开辟步骤:

0804855f <main>:


 8048560:  89 e5                      mov    %esp,%ebp  //更新栈基址为当前栈指针所指地址

 8048562:  83 e4 f0                    and    $0xfffffff0,%esp // 栈指针地址16字节对齐

 8048565:  83 ec 20                    sub    $0x20,%esp  //栈指针向下偏移32个字节

 8048568:  c7 44 24 08 03 00 00   movl   $0x3,0x8(%esp)  //初始化middle函数参数p2 = 0x03

 804856f:  00                                                                      

 8048570:  c7 44 24 04 02 00 00   movl   $0x2,0x4(%esp)  //初始化p1 = 0x02

 8048577:  00

 8048578:  c7 04 24 01 00 00 00   movl   $0x1,(%esp)  //初始化p1 = 0x01

 804857f:  e8 00 ff ff ff          call   8048484 <middle>  //调用middle函数

左侧是内存地址,中间的数字是指令机器码,右侧是mian函数调用middle函数之前的汇编指令,x86堆栈是向下增长的(即:高地址向地地址增长)指令:sub    $0x20,%esp,堆栈指针向下偏移0x20,其实就是开辟一个 32字节的栈帧空间,所以函数开辟栈帧的方式就是通过偏移栈指针的方式。由汇编文件看返回地址并没有在栈帧空间中保存,因为函数本身并不知调用它的函数是谁,所以也就不知道返回地址。函数栈帧空间是ebp和esp所指的空间范围。

​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

缥缈孤鸿_jason

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值