1 int a(int x)
2 {
3 return x + 5;
4 }
5
6 int b(int x)
7 {
8 return a(x);
9 }
10
11 int main(void)
12 {
13 return b(5) - 2;
14 }
可以看到程序中有很多函数的调用和返回。为什么要这样设置呢?因为程序中的函数调用时计算机工作运行的关键,分析函数调用的具体实现能够帮助理解计算机运行的原理。
gcc -S -o main.s main.c -m32
命令生成汇编代码。结果如下图。后面加-m32是为了让其按照32位的方式反汇编。
我们只需要看汇编代码的关键部分,可以把点开头的语句全部删去,得到如下的汇编指令。
1 a:
2
3 pushl %ebp
4 movl %esp, %ebp
5 movl 8(%ebp), %eax
6 addl $5, %eax
7 popl %ebp
8 ret
9
10 b:
11
12 pushl %ebp
13 movl %esp, %ebp
14 subl $4, %esp
15 movl 8(%ebp), %eax
16 movl %eax, (%esp)
17 call a
18 leave
19 ret
20
21 main:
22
23 pushl %ebp
24 movl %esp, %ebp
25 subl $4, %esp
26 movl $5, (%esp)
27 call b
28 subl $2, %eax
29 leave
30 ret
接下来我们分析C代码和汇编程序究竟是如何对应起来的,以及汇编语言是如何工作的。
我们先看C程序,从main函数看起,它返回了一个函数b再进行运算的结果。那么我们来看函数b,它返回的是函数a的结果,而函数a的作用是将传递给它的参数x加5。所以对于这个程序,最后得到的数值应该是5+5-2=8。
再来看汇编代码,我们还是从main函数看起,一条指令一条指令地分析。第n条表示指令执行的顺序,后面列出了代码的行号和执行的指令。
第1条:23 pushl %ebp
一看到push,我们就知道这是在对栈进行操作。ebp是栈顶指针,esp是栈当前位置指针,栈是自上向下生长的,后进先出。先把ebp压栈,实际上是先将esp-4再将ebp放到栈当前位置。
第2条:24 movl %esp, %ebp
将esp的值放到ebp中,也就是说现在ebp的指向改变为esp的指向。
第3条:25 subl $4, %esp
将esp-4。
第4条:26 movl $5, (%esp)
将5移入esp指向的地址中。
第5条:27 call b
调用函数b,这里等于两个操作,一个是先将现在的eip入栈,此时eip应为subl $2,%eax这条指令的位置,我们记为28。另一个操作是将b函数的地址放入eip,也就是说此时程序要从10开始执行。
第6条:12 pushl %ebp
第7条:13 movl %esp, %ebp
第8条:14 subl $4, %esp
此时已跳转到b函数,指令之前已经讲过了,与7、8条一起不再赘述。
第9条:15 movl 8(%ebp), %eax
movl 8(%ebp), %eax,是将ebp的值+8指向的内容放入eax,实际上就是eax = 5。
第10条:16 movl %eax, (%esp)
将eax的内容放入现在esp指向的内容中。
第11条:17 call a
调用函数a,与前面的步骤类似。
第12条:3 pushl %ebp
第13条:4 movl %esp, %ebp
第14条:5 movl 8(%ebp), %eax
是a函数的pushl %ebp,与13、14条一同省略。
第15条:6 addl $5, %eax
将eax中的值+5得到10。
第16条:7 popl %ebp
将现在esp指向的内容放入ebp,esp+4,所以现在ebp=4。
第17条:8 ret
是ret,即popl %eip,也就是现在的eip更改为18,回到函数b,从leave开始执行。
第18条:18 leave
leave,表示两条指令,movl %ebp,%esp和popl %ebp。
第19条:19 ret
ret回到main函数,从28处执行。
第20条:28 subl $2, %eax
将eax中的内容-2,即8。
第21条:29 leave
第22条:30 ret
如图所示。从图中我们可以看到,栈又回到了初始的位置。
至此,汇编代码就分析完了。
从上面的过程可以看出,计算机最本质的工作原理,是对存储的数据进行处理,并把结果保存,然后不断循环这个处理数据的过程。指令就是对数据进行处理的依据。具体的方法就是借助CPU中的寄存器,以及内存中的栈,依据一个约定的步骤对数据进行操作。计算机其实很简单,它是一个认死理的家伙,只要确定了每一步要做什么,它就会严格地按照步骤把操作完成,绝对不打折扣。因此,相比与人打交道,与计算机打交道可是要轻松多了。