函数调用的汇编流程

测试环境:Windwos 7 + GCC 4.5.2 Target: mingw32

测试代码:

   1: code.c:
   2:  
   3: int accum = 0;
   4:  
   5: int sum(int x, int y)
   6: {
   7:     int t = x +y;
   8:     accum += t;
   9:     return t;
  10: }
  11:  
  12: main.c:
  13:  
  14: int main()
  15: {
  16:     return sum(1, 3);
  17: }

测试流程:

1. > gcc –O2 –S code.c

生成code.s 部分省略

   1: _sum: 
   2:     pushl    %ebp 
   3:     movl    %esp, %ebp 
   4:     movl    12(%ebp), %eax 
   5:     addl    8(%ebp), %eax 
   6:     addl    %eax, _accum 
   7:     leave 
   8:     ret 

可以注意到: sum函数首先保存ebp, 然后把当前esp 存入ebp, 因为esp寄存器存放当前栈顶指针,所以根据ebp的偏移获取参数x, y. (注意因为push ebp后,所以偏移为 12, 8, leave指令相当于 movl %ebp,%esp  popl %ebp, 对应的还有ENTER指令)

2 >gcc –O2 –c code.c

>objdump –d code.o

得到输出

   1: 00000000 <_sum>: 
   2:    0:   55                      push   %ebp 
   3:    1:   89 e5                   mov    %esp,%ebp 
   4:    3:   8b 45 0c                mov    0xc(%ebp),%eax 
   5:    6:   03 45 08                add    0x8(%ebp),%eax 
   6:    9:   01 05 00 00 00 00       add    %eax,0x0 
   7:    f:   c9                      leave 
   8:   10:   c3                      ret 
   9:   11:   90                      nop 
  10:   12:   90                      nop 
  11:   13:   90                      nop

注意这里 accum 的地方地址为全0,还没有最后确定。

3 >gcc –O2 –o prog code.o main.c

> objdump –d prog > tmp.txt

生成tmp.txt约1193行,查找到相关汇编码:

   1: 004013c0 <_sum>: 
   2:   4013c0:    55                       push   %ebp 
   3:   4013c1:    89 e5                    mov    %esp,%ebp 
   4:   4013c3:    8b 45 0c                 mov    0xc(%ebp),%eax 
   5:   4013c6:    03 45 08                 add    0x8(%ebp),%eax 
   6:   4013c9:    01 05 20 50 40 00        add    %eax,0x405020 
   7:   4013cf:    c9                       leave  
   8:   4013d0:    c3                       ret    
   9:   4013d1:    90                       nop 
  10:   4013d2:    90                       nop 
  11:   4013d3:    90                       nop
  12:  
  13: 004013d4 <_main>: 
  14:   4013d4:    55                       push   %ebp 
  15:   4013d5:    89 e5                    mov    %esp,%ebp 
  16:   4013d7:    83 e4 f0                 and    $0xfffffff0,%esp 
  17:   4013da:    83 ec 10                 sub    $0x10,%esp 
  18:   4013dd:    e8 3e 06 00 00           call   401a20 <___main> 
  19:   4013e2:    c7 44 24 04 03 00 00     movl   $0x3,0x4(%esp) 
  20:   4013e9:    00 
  21:   4013ea:    c7 04 24 01 00 00 00     movl   $0x1,(%esp) 
  22:   4013f1:    e8 ca ff ff ff           call   4013c0 <_sum> 
  23:   4013f6:    c9                       leave  
  24:   4013f7:    c3                       ret    
  25:     ...

发现: 这时候 accsum处已经填入了真实地址,不过main函数内的行为有点奇怪,首先保存ebp和esp, 然后清空低4位的%esp, 加上16,调用___main, 而___main处理信息有些奇怪,判断 %405060地址(由accsum的地址知道那应该是全局变量区附近)处内容是否为空来决定是否调用

401a3d:    eb 81                    jmp    4019c0 <___do_global_ctors>

不过与我们现在的讨论无关,先行跳过,之后到Linux上去调试看看。

之后的送入参数,奇怪的是 sub 0x10 把栈增加了16个长度后只使用最上面的8个。

最后执行调用,值得注意的是,无论是被调用函数还是调用函数都不再负责保持堆栈平衡,只要使用esp,ebp反复恢复上一次的栈顶情况就可以了。

此条目发表在C, 系统, 编程分类目录,贴了, , 标签。将固定链接加入收藏夹。

发表评论

邮箱地址不会被公开。 必填项已用*标注