C语言的编译系统

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

从源代码到可执行的目标程序,除了编译器之外往往还需要预处理器、汇编器和链接器。而想让目标代码运行还需要一些工具的支撑,如动态连接程序、无用单元收集程序等,这些工具的集合称为运行系统。

就C 语言而言,其源代码往往分成若干个模块而存储在不同的源文件中,C 编译系统对这些源文件分别进行预处理、编译、汇编过程以形成可重定位的目标文件;然后再使用链接器将这些可重定位的目标文件和必要的库文件链接成一个可执行的目标文件,即具有绝对地址的机器代码,如图:

大多数编译系统提供一个驱动程序来调用预处理器、编译器、汇编器、链接器,以便支持用户完成从源程序到可执行程序的整个开发过程,例如典型的GCC 。

预处理器主要完成文件包含、宏的替换、条件编译等工作。

编译器对预处理器后的C 语言代码进行词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价、优化的中间代码表示或汇编代码。

汇编器主要负责①将表示存储单元的所有标识符都存入符号表并分配地址②将每个操作码译为目标机器代表该操作的二进制位串,并把代表存储单元的每个标识符翻译成符号表中这个标识符所分配的地址。我们必须明确,由于许多没有解决的问题(例如一个汇编文件中存在外部符号的引用,即某个源文件引用了另一源文件中定义的某个符号,如变量或者函数调用等;或者在程序中调用了某个库文件中的函数),所以由汇编器生成的除平坦二进制文件外的其他目标文件都不能立即执行,所有的这些问题都需要经链接器的处理才能解决。

链接是一个收集、组织程序所需的不同代码和数据(因为他们可能在不同的目标文件中),以便程序能被装入内存并被执行的过程。这样,链接器的主要工作就是将有关的目标文件彼此相连接,将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。

这样,我们就知道目标文件由汇编器或链接器创建,实际上主要分为可重定位的目标文件和可执行的目标文件有两种(还有共享的目标文件):

可重定位的目标文件包含二进制代码和数据,可以和其他可重定位目标文件链接成一个

可执行的目标文件或更大的可重定位的目标文件

预处理器cpp 源程序 修改后的源程序 *.i 可重定位的目标程序 *.o 库

可重定位的目标文件

编译器ccl 汇编器as 汇编程序 *.s 连接器ld 可执行的目标程序

可执行的目标文件包含二进制代码和数据,可以直接被复制到内存执行,类似于shell 脚本等需要抽象机制辅助的程序是不被接受的。

当然,目标文件的格式随操作系统的不同而不同,主要有以下几种:

COM,用于DOS操作系统,平坦的二进制形式

a.out,用于早期UNIX操作系统,现代UNIX下的可执行文件仍然被缺省地命名为a.out COFF(Common Object File Format),用于System V UNIX操作系统

PE(Portable Executable),用于Win NT操作系统,从COFF中衍生

ELF(Executable and Linkable Format),用于现代UNIX操作系统中,作为应用程序二进制接口Application Binary Interface而开发,为工作在相同体系结构上不同操作系统提供可移植的二进制文件

下面我们对链接器进行着重说明。我们已经知道,链接是一个收集、组织程序所需的不同代码和数据(因为他们可能在不同的目标文件中),以便程序能被装入内存并被执行的过程。实际上,如果这些目标文件以有用的方式组织在一起,那么他们之间必然会出现一些外部引用,这种引用可以是定义在一个文件而使用在另一个文件的数据单元,也可以是入口点出现在一个文件而调用点出现在另一个文件中的函数。从以上的两点我们可以看出,链接器其实主要完成了两个任务,即符号解析和重定位。符号解析是为了识别并关联所有的符号,而重定位是为了将所有从零地址开始的相对地址重定位为绝对地址。

为了建立对符号解析和重定位的深刻理解,我们必须首先来看一个背景知识:对于编译器/汇编器而言,当遇到当前源文件没有定义的符号时,它们就假定该符号在其他某个模块中定义,并为该符号产生一条符号表条目,把它留给链接器处理;如果链接器在所有的输入模块中都找不到被引用符号的定义,那么就会出错。

这样我们就能给出符号解析和重定位的准确定义了,所谓符号解析(symbol resolution),即识别各个目标模块中定义和引用的符号,为每一个符号引用确定它所关联的一个同名符号的定义。更具体地说,就是链接器需要将每个符号引用正确地与某可重定位模块的符号表中的一个符号定义相关联,从而确定各个符号引用的位置。在链接器的上下文中,一个重定位模块M可能定义和引用的符号通常有三类,即:

全局符号,那些在模块M中定义,可以被其它模块引用的符号,包括模块M中定义的非static属性的函数和全局变量

局部符号,指那些在模块M中定义,且只能在本模块中引用的符号,包括模块M中定义的有static属性的函数和全局变量

外部符号,指那些由模块M引用并由其它模块定义符号

而所谓的重定位是因为汇编器产生的代码段和数据段都是从零地址开始的,链接器为了将这些从零地址开始的代码段和数据段组织成一个文件,就需要重定位这两个节,其具体方法是将每一个符号定义关联到一个内存位置,然后修改所有对这些符号的引用,使他们指向相关联的内存位置。

链接可以在三种情况下进行,即编译时、装入时和运行时进行,其中编译时链接在将源代码翻译成机器代码的过程中完成,装入时链接在程序装入内存并执行时完成,运行时链接在程序运行时完成。这样也就引出了静态链接器和动态链接器的概念,所谓静态链接器即将多个可重定位的目标文件组成一个可执行的目标文件/可重定位的目标文件的工具;而动态链接器用于在内存中的可执行程序执行时与共享目标文件/动态链接库进行动态链接。

相关文档
最新文档