【计算机系统基础】预处理、编译、汇编、链接概述

一、步骤解读(预处理,编译,汇编,链接)

①.c -> .i:.i本质上还是高级语言文件,.i文件中不包括任何宏定义

然后,对.i(预处理后的文件)进行词法、语法、语义分析【就是编译】,优化后生成汇编代码文件

②.i -> .s:.s还是文本文件(汇编语言文件,人可读,机器不可读)

③.s -> .o:对汇编语言程序进行汇编,得到可重定位目标文件.o。.o不是文本了,是机器指令序列(也叫做机器语言程序、机器代码,由计算机直接识别)。

【注】机器指令和汇编指令是一一对应的。

④.o ->(无后缀) :与库函数(许多.o可重定位目标文件)进行链接,得到可执行程序(如下图,本质上是二进制代码的合并)

【注意】操作之间的关系

预处理、编译和汇编针对一个.c文件进行,得到一个对应的.o文件。

链接是将多个可重定位目标文件合并,生成可执行目标文件。

 

【链接命令举例】

$gcc -static -o (自定义的可执行程序的文件名) main.o test.o(静态链接)

 

二、预处理的步骤(和#密切相关,注意:#pragma要保留!)

【例子】条件编译指令

如图,根据INITIALIZE是否有定义,预处理后得到不同的源程序。

 

三、编译、汇编

1、汇编语言出现后:用助记符表示操作码、寄存器,符号表示位置 。

子程序( 函数)的起始地址和 变量起始地址是符号定义(definition)
调用子程序(函数或过程)和 使用变量即是符号的引用(reference)

图中,L0是一个地址,属于符号定义。而调用一个符号对应的函数(如图中jmp L0)就是一个符号的引用。

注意:符号的引用可以在不同模块间使用,也就是说,一个模块定义的符号可以被另一个模块引用。链接完成之后,才是一个完成的程序。

链接的优点:a.模块化  b.效率高(分开编译,重新编译被修改的源程序文件,再重新链接即可)

【练习】指出哪些是定义,哪些是引用

【解答】

1、红框内是符号定义,蓝框内是符号的引用。所以一般规律是,只要在函数或变量名前面有类型(void int等),就是个符号定义。

2、虽说左右两个swap都是定义,但是左端的swap声明,仅仅用以说明swap是一种外部定义,是一种弱定义,在main函数找不到它的“真正的定义”。右端的swap则是强定义。要区分开!

3、(易错)int 后的temp和“=”左的temp不是符号定义或符号的引用,因为temp在栈中,只能在内部使用,不能被外部引用:

因此,“符号”肯定不能是非静态局部变量!

从本题还可知:定义和引用可以不在一个程序模块(.c文件)中!

 

四、链接(本章重点

1、以 $ gcc -O2 -g -o test main.c test.c为例
在最后的链接过程中,main.o,test.o,printf.o(库函数)等等链接成一个可执行文件test
 
【区分下面的两种文件的差异(都是以test.c为基本)】
 
【test.o的反汇编文件( objdump -d test.o)
<add>: 
 0: 55            push %ebp
 1: 89 e5         mov %esp, %ebp
 3: 83 ec 10      sub $0x10, %esp
 6: 8b 45 0c      mov 0xc(%ebp), %eax
 9: 8b 55 08      mov 0x8(%ebp), %edx
 c: 8d 04 02      lea (%edx,%eax,1), %eax
 f: 89 45 fc      mov %eax, -0x4(%ebp)
12: 8b 45 fc      mov -0x4(%ebp), %eax
15: c9            leave 
16: c3            ret

【test的反汇编文件(objdump -d test )】

80483d4<add>:
80483d4: 55        push %ebp
80483d5: 89 e5     mov %esp, %ebp
80483d7: 83 ec 10  sub $0x10, %esp
80483da: 8b 45 0c  mov 0xc(%ebp), %eax
80483dd: 8b 55 08  mov 0x8(%ebp), %edx
80483e0: 8d 04 02  lea (%edx,%eax,1), %eax
80483e3: 89 45 fc  mov %eax, -0x4(%ebp)
80483e6: 8b 45 fc  mov -0x4(%ebp), %eax
80483e9: c9        leave 
80483ea: c3        ret

【结论】指令等都相同。但两种文件的起始地址不同,test的反汇编文件是虚拟地址

 

2、链接的本质

每个.o文件中都有若干个基本区:如代码区和数据区。如在main.o中,有代码区、数据区。每个.o文件都有确定的格式来组织。这些区又称为。合并相同“节”,就可以得到可执行目标文件。

其中,.bss(未初始化变量)和.data(初始化变量)都对应数据读写段,.text对应只读代码段。段的概念后续会涉及。

 

3、链接的步骤(符号解析+重定位)

①确定标号引用关系(符号解析

②合并相关.o文件(代码区和代码区合并,数据区和数据区合并……)

【如下图】(我们知道每个.o文件的起始地址是0)合并之后到虚拟地址区域时,每个标号的偏移量相同(比方说在p0.o里,B,C相对于0的偏移量,与链接后相对于虚拟地址的偏移量不变,但地址变化

故:

③确定每个标号的地址

④在指令中写入新地址(这就是“可重定位”的含义)

 

Step1.符号解析(对应①)

先找到定义和引用的符号,比如:

void swap() {…} /* 定义符号swap */
swap(); /* 引用符号swap */
int *xp = &x; /* 设xp为静态变量,则本行定义符号 xp, 引用符号 x */

编译器会把定义的符号放在符号表(一种结构数组)中:

表项里包括了符号名、长度、位置等信息。

 

Step2.重定位(对应②③④)

合并节形成段、计算在存储器映像的虚拟地址(绝对地址)、引用的地址改成重定位后的地址信息。

 

4、可执行文件在存储器(解释程序在存储器中怎么存储

如图,黄框内容映射在“只读代码段”,蓝框内容映射在“读写数据段”。也就是:这两个段的内容都由可执行目标文件导入。

所有程序的虚拟地址的起始都是0x08048000(这是最低地址,当然,也可能从比该地址高的地方开始导入段).

最后真正运行还要进行一次转换(第六章有阐述)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值