编译和链接-目标文件格式
目标文件是编译器编译源代码后生成的文件,从结构上讲,它具有可执行文件的文件格式,但是没有经过连接过程,因此对于其他编译模块的引用还没有连接上,本文将简单介绍目标文件的文件格式,对目标文件格式的理解是下文介绍静态链接和动态链接的基础。
可执行文件格式类型
目标文件是源代码编译后的文件(Windows下的.obj和Linux下的.o),他和可执行文件的格式是相同的,可执行文件格式主要是Windows下的PE(Portable Executable)和Linux下的ELF(Executable Linkable Format)。
除此之外,动态链接库(DLL, Dynamic Linking Library)和静态链接库(Static Linking Library)也是可执行文件格式存储的。ELF格式的文件归为一下四类:
ELF文件类型 | 说明 | 实例 |
目标文件 | 可连接为可执行文件或共享目标文件,静态链接库也属于此类 | Linux:.o Windows: .obj |
可执行文件 | 可直接执行的文件 | Bin/bash文件 Exe文件 |
共享目标文件 | 动态链接库 | .so .dll |
核心转储文件 | ? | ? |
目标文件格式
在目标文件中,需要存储编译后的机器指令的指令代码以及数据,还有连接需要的信息,如符号表、字符串等。目标文件将不同的信息放在不同的地方,分"段"(segment)存储。
如下图所示,目标文件中最基本最重要的有以下几个段:代码放在代码段(.text段)中,全局变量和静态变量放在数据段(data段)中,而为初始化的全局和静态变量定义放在.bbs段中,因为他们都默认为0,表示为为初始化的全局变量和静态变量预留位置。
图 1
综上所述,程序源代码代码主要分为两种段:程序指令和程序数据。原因和好处有:
- 可设为一个只读,一个可读写。
- 提高程序的局部性,提高缓存的命中率。
- 同时运行多个程序副本时,只需要存储一个程序指令的代码段数据。
ELF文件结构描述
上文是一个ELF文件(目标文件属于ELF文件)的大致介绍,下文将详细介绍ELF文件的格式。下图描述了ELF的总体结构,本图只显示一些重要的结构。
文件头
ELF最前面的还是ELF文件头,描述了整个文件的基本属性,如ELF魔数、文件机器字节长度、数据存储方式、ELF重定位类型、硬件平台、入口地址、程序头入口、段表的位置和长度以及段的数量。
段表
ELF文件中有各种各样的段,而段表(Section Header table)保存了这些段的基本属性,如段名、长度、文件中的偏移等。段表的位置由头文件中的信息确定,而其他段的位置由段表确定。
重定位表
在我们举的对象文件的例子(图1)中,会产生一个".rel.text"的段,它就是一个重定位表。连接器在处理目标文件时,目标文件对其他模块的对象的引用需要进行重定位,这些信息都记录在ELF文件的重定位表中。对于每一个需要重定位的代码段或者数据段,都有一个相应的重定位表。比如".rel.text"就是对".text"段的重定位表,如我们的例子中有一个对printf函数的调用,因此.text段有一个绝对地址的引用。
重定位表在链接过程中具有重要的意义。
字符串表
ELF文件中用到了很多字符串,比如段名、变量名等。因为字符串长度不同,为了使用固定的结构表示,ELF文件将字符串集中起来存放,然后使用字符串在表中的偏移量来引用字符串。
常见的段名为.strtab和.shstrtab,分别代表字符串表和段表字符串表,前者保存普通的字符串,后者保存段名。
符号——链接的接口
链接的本质是将相互调用的目标文件进行组装。如目标A文件中的函数f()被目标文件B调用,连接就是将他们连接起来。在链接中,我们将函数和变量成为符号,他们的名字是符号名。
每一个目标文件都会有一个符号表,记录了目标文件中所使用的所有文件。每个符号有一个对应的符号值,对于变量和函数来说,符号值就是他们对应的地址。
符号主要有以下几种:
- 定义在目标文件的全局符号,可以被其他目标文件引用。如全局变量和函数。
- 本目标文件中引用的其他文件的全局符号,一般叫外部符号。如printf函数。
- 段名,符号由编译器产生,它的值就是起始地址。如.text.
- 局部符号,只在编译单元内可见。如局部static变量。
- 行号信息。
其中,链接过程最关心全局符号的粘合,所以最重要的就是第一类和第二类全局信息
符号表一般是ELF文件的一个段.symtab。每个符号有一个结构,存储的信息包括:符号名、符号值、符号大小、符号类型和绑定信息,符号所在段。
其中,符号类型有:
NOTTYPE | 未知符号类型 |
OBJECT | 数据对象,如变量、数组 |
FUNC | 函数 |
SECTION | 段 |
FILE | 文件名。 |
绑定信息有
LOCAL | 局部符号 |
GLOBAL | 全局符号 |
WEAK | 弱引用 |