ELF 文件格式
ELF(Executable and Linking Format,可执行连接格式)是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。工具接口标准委员会(TIS)选择了正在发展中的ELF标准作为工作在32位Intel体系上不同操作系统之间可移植的二进制文件格式。如果开发者定义了一个二进制接口集合,ELF标准用它来支持流线型的软件发展。 应该减少不同执行接口的数量,因此就可以减少由于重新编程所需要重新编译的代码。
在Linux下开发程序可以使用ELF格式作为可执行程序、库以及程序生成过程的中间文件。这与ELF文件格式包括三种主要的类型对应。
可执行文件(应用程序):可执行文件包含了代码和数据,是可以执行的程序。
可重定向文件(*.o):可重定向文件包含了代码和数据(它们是和其他重定位文件和共享的object文件一起连接时使用的)。
共享object文件(*.so):包含了代码和数据,它们是在连接时被连接器ld和运行时动态连接器使用的。动态连接器可能称为ld.so.1,libc.so.1 或者 ld-linux.so.1。
object文件参与程序的连接(创建一个程序)和程序的执行(运行一个程序)。object 文件格式提供了一个方便有效的方法,以并行的视角看待文件的内容, 在它们的活动中,反映出不同的需要。
一个ELF头在文件的开始,保存了路线图(road map),描述了该文件的组织情况。 sections保存着object 文件的信息,从连接角度看:包括指令、数据、符号表、重定位信息等。
程序头部表(program header table)告诉系统如何建立一个进程映像.它是从加载执行的角度来看待ELF文件的。从它的角度看,ELF文件被分成许多段,ELF文件中的代码、连接信息和注释都以段的形式存放。如果一个程序头部表存在,那么它告诉系统如何来创建一个进程的内存映像。被用来建立进程映像(执行一个程序)的文件必须要有一个程序头部表;可重定位文件不需要这个头部表。
节头部表(section header table)包含了描述文件sections的信息。每个section在这个表中有一个入口;每个入口给出了该section的名字、大小等信息。在连接过程中的文件必须有一个节头部表;其他object文件可要可不要这个节头部表。
一个ELF文件从连接器(linker)的角度看,是一些节(sections)的集合;从程序装载器(loader)的角度看,它是一些段(segments)的集合。ELF格式的程序和共享库具有相同的结构,只是段的集合和节的集合上有些不同。
ELF格式文件中的各个节区如下所示。
.text:包含程序的可执行指令。
.rodata和.rodata1:只读数据,这些数据通常参与进程映像的不可写段。
.data和.data1:初始化了的数据(可写),将出现在程序的内存映像中。
.bss:包含将出现在程序的内存映像中的未初始化数据。根据定义,当程序开始执行时,系统将把这些数据初始化为 0。此节区不占用文件空间。
.init:可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在开始调用主程序入口之前执行这些代码。
.fini:此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时执行这里的代码。
.relaname和.relaname:重定位的信息。如果文件中包含可加载的段,段中有重定位内容,则需要在执行过程中占用内存。
.got和.plt:全局偏移表和过程连接表。
.dynamic:动态连接信息。
.comment和.debug:版本控制信息和调试的信息。
.dynstr:动态连接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。
.hash:一个符号哈希表。
.interp:程序解释器的路径名。
.line和.note:分别表示符号调试的行号信息和注释信息。
.shstrtab:节区名称。
.strtab:字符串,通常代表与符号表项相关的名称。
.symtab:符号表,如果文件中包含一个可加载的段,则包含此节。
凡是以“.”开头的节区是系统保留的,目标文件中还可以使用自定义的节区,各个体系结构和编译器也可以自定义节区。
ELF格式的文件还使用进程环境中名为 LD_LIBRARY_PATH 的变量,表示连接所使用的连接库的各个路径。其中包含若干用“:”分隔的路径名,以“;”为结尾。
PIC表示“与位置无关的代码”(position-independent code),是ELF支持的特殊格式的代码。在gcc编译的时候,可以使用-fPIC指示GNU编译系统生成PIC代码。PIC是实现共享库或共享可执行代码的基础。这种代码的特殊性在于它可以加载到内存地址空间的任何地址执行。这也让加载器可以很方便地在进程中动态连接共享库。
GOT表示“全局偏移量表”,(global offset table),表示代码段中任何指令和数据段中的任何变量之间的距离,都是一个与代码段和数据段的绝对存储器位置无关的常量。因此,编译器在数据段开始的地方创建了一个GOT表。GOT包含每个被这个目标模块引用的全局数据目标的表目。编译器还为GOT中每个表目生成一个重定位记录。在加载时,动态连接器会重定位GOT中的每个表目,使得它包含正确的绝对地址。PIC在代码中实现通过GOT间接地引用每个全局变量。
版权声明: 本文为 InfoQ 作者【韩超】的原创文章。
原文链接:【http://xie.infoq.cn/article/f4568ff36120ffcb452effae0】。文章转载请联系作者。
评论