先做个更正,之前提到的代码地址,其实是汇编代码的首位数字,正规名应该是程序计数器才对O(∩_∩)O~
一:编译器对寄存器的使用规则
在第(2)节的最后,我们讨论了最简单的函数调用ebpesp.c的汇编实现,这里我们做一个小改,增加一句打印sum的操作:
int son_add(int a, int b)
{
return a+b;
}
int father()
{
int a = 1;
int b = 2;
int sum = 0;
sum = son_add(a, b);
printf("%d\n", sum);
return sum;
}
00000000 <son_add>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 0c mov 0xc(%ebp),%eax
6: 03 45 08 add 0x8(%ebp),%eax
9: c9 leave
a: c3 ret
b: 90 nop
0000000c <father>:
c: 55 push %ebp
d: 89 e5 mov %esp,%ebp
f: 53 push %ebx
10: 50 push %eax
11: 6a 02 push $0x9
13: 6a 01 push $0x8
15: e8 fc ff ff ff call 16 <father+0xa>//调用son_add
1a: 50 push %eax
1b: 89 c3 mov %eax,%ebx
1d: 68 00 00 00 00 push $0x0
22: e8 fc ff ff ff call 23 <father+0x17>//调用printf
27: 89 d8 mov %ebx,%eax
29: 8b 5d fc mov 0xfffffffc(%ebp),%ebx
2c: c9 leave
2d: c3 ret
有几个遗留问题未说明,比如,son_add()在计算出sum的和之后,通过什么方式返回给father?千万别盯着ret看,在(2)节已经写出我YY的ret等价汇编伪代码,里面丝毫没提返回值的事。
仔细观察son_add函数程序计数器6的操作,计算的和被赋值给寄存器%eax,GCC默认用%eax来存储函数调用的返回值,由于增加了对sum进行打印,father函数在call调用后执行:push %eax,将返回值%eax压入栈,这步应该是为之后的printf函数调用做参数准备(son_add的返回值作为printf的参数),紧接着mov %eax,%ebx,这一步是将son_add的返回值备份到%ebx中,我猜测可能是因为之后的printf函数调用也会产生返回值而像son_add那样修改%eax,因此才需要用%ebx保存%eax&#