测试环境: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反复恢复上一次的栈顶情况就可以了。