程 序 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
学过计算机的都知道,我们将c语言编译以后就成了汇编语言,汇编语言作为一种更加接近计算机的语言,分析它有助于我们理解计算机的工作过程。在Linux系统中,通过gcc –S –o main.s main.c语句即可将c语言文件转换成汇编文件。
学好汇编离不开分析汇编文件,下面我们来结合一个小的c语言程序,将其编译成汇编语言,结合堆栈变化,一起来看看c语言的执行过程。在开始讲解前,我们一起来了解一下基本知识。esp:指针寄存器,指向栈顶。
ebp:指针寄存器,指向栈底。
eip:指令寄存器,存放下一条执行语句地址。
eax:数据暂存寄存器,暂时存放相应的执行结果。
movl:移动指令,将前面的值放入后面,注意此处有多种寻址方式。
subl:汇编减法语句,后面减去前面数值放入后面寄存器。
addl:汇编加法语句,后面加上前面数值放入后面寄存器。
leave:汇编语句,相当于movl %ebp ,%esp ; popl %ebp两条语句的结合。
enter: 汇编语句,相当于 pushl %ebp;movl %esp,%ebp 两条语句的结合。
popl:汇编出栈语句,相当于movl (%esp),%eax; addl $4 ,%esp两条语句的结合。
pushl:汇编压栈语句,相当于 subl $4,%esp;movl %eax,(%esp) 两条语句的结合。
call:汇编调用语句,相当于pushl %eip(*) ; movl $0x12345,%eip(*)两条语句的结合。
ret:汇编返回语句,相当于Popl %eip(*)。
下面开始介绍c语言变成汇编语言后是怎么运行的。
首先我们来看c语言,凡是有一定c语言基础知识的人,理解下面一段c语言程序代码都不是很困难。c语言代码如下
1 int g(int x)
2 {
3 return x+4;
4 }
5
6 int f(int x)
7 {
8 return g(x);
9 }
10
11 int main(void)
12 {
13 return f(5)+1;
14 }
结果很简单,我们知道结果为10。下面来看看这段代码转换成汇编语言后是一个什么情况。源汇编代码较为复杂,因为其中保留了很多链接信息,但是那些语句不会执行,为了简便,我将里面负责链接的语句删除了。上面c语言的程序的汇编代码如下
1 g:
2 pushl %ebp
3 movl %esp, %ebp
4 movl 8(%ebp), %eax
5 addl $4, %eax
6 popl %ebp
7 ret
8 f:
9 pushl %ebp
10 movl %esp, %ebp
11 subl $4, %esp
12 movl 8(%ebp), %eax
13 movl %eax, (%esp)
14 call g
15 leave
16 ret
17 main:
18 pushl %ebp
19 movl %esp, %ebp
20 subl $4, %esp
21 movl $5, (%esp)
22 call f
23 addl $1, %eax
24 leave
25 ret
为了方便讲解,我们来约定一些规则,约定规则如图1所示,假设下面是此段c程序的堆栈段,我们用逻辑地址0-10表示实际物理地址,然后esp,ebp两个寄存器指针指向0处,现在开始运行了,我们用表格动态模拟堆栈变化。
下面这张表格记录了程序的运行过程。
表1.汇编代码运行时的寄存器及堆栈变化表
至此,我们了解清楚了c语言编译成汇编语言后在机器平台上面的运行流程和方式。下面,我来演示一下Linux系统下的实现。
图2.在Linux环境下进入Code文件夹
图3.用vi编辑器进行编辑c文件
图4.编写的c文件
图5.运行指令进行编译
图7.保留我们需要分析的代码段
总结:通过以上过程,我们应该对计算机是怎么工作的有一定的了解。人类的思维活动通过相应的高级语言编写成代码,输入到计算机中,计算机根据规则将高级语言代码转化成汇编语言,然后通过取址、译码、运算、输出结果等步骤将人类赋予的任务完成。从人类抽象思维到计算机具体的加减乘除的转化,再将一串二进制数字转化为人类能够理解的符号,即是计算机的意义,这其中的转化流程即是计算机的工作过程。