1. 概述
Executable and linking format(ELF)文件是x86 Linux系统下的一种常用目标文件(object file)格式,有三种主要类型:
(1)适于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。
(2)适于执行的可执行文件(executable file),用于提供程序的进程映像,加载的内存执行。
(3)共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
ELF文件格式比较复杂,本文只是简要介绍它的结构,希望能给想了解ELF文件结构的读者以帮助。具体详尽的资料请参阅专门的ELF文档。
2. 文件格式
为了方便和高效,ELF文件内容有两个平行的视角:一个是程序连接角度,另一个是程序运行角度,如图1所示。
ELF header在文件开始处描述了整个文件的组织,Section提供了目标文件的各项信息(如指令、数据、符号表、重定位信息等),Program header table指出怎样创建进程映像,含有每个program header的入口,Section header table包含每一个section的入口,给出名字、大小等信息。
图1
3. 数据表示
ELF数据编码顺序与机器相关,数据类型有六种,见表1。
4. ELF文件头
象bmp、exe等文件一样,ELF的文件头包含整个文件的控制结构。它的定义如下:
#define EI_NIDENT 16 typedef struct{ unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; }Elf32_Ehdr; |
其中E_ident的16个字节标明是个ELF文件(7F+'E'+'L'+'F'+class +data+version+pad)。E_type表示文件类型,2表示可执行文件。E_machine说明机器类别,3表示386机器,8表示MIPS机器。E_entry给出进程开始的虚地址,即系统将控制转移的位置。E_phoff指出program header table的文件偏移,e_phentsize表示一个program header表中的入口的长度(字节数表示),e_phnum给出program header表中的入口数目。类似的,e_shoff,e_shentsize,e_shnum 分别表示section header表的文件偏移,表中每个入口的的字节数和入口数目。E_flags给出与处理器相关的标志,e_ehsize给出ELF文件头的长度(字节数表示)。E_shstrndx表示section名表的位置,指出在section header表中的索引。
下面有个elf文件头的例子,可以对照理解,见图2。
图2
5. section header
目标文件的section header table可以定位所有的section,它是一个Elf32_Shdr结构的数组,Section头表的索引是这个数组的下标。有些索引号是保留的,目标文件不能使用这些特殊的索引。
Section包含目标文件除了ELF文件头、程序头表、section头表的所有信息,而且目标文件section满足几个条件:
(1)目标文件中的每个section都只有一个section头项描述,可以存在不指示任何section的section头项。
(2)每个section在文件中占据一块连续的空间。
(3)Section之间不可重叠。
(4)目标文件可以有非活动空间,各种headers和sections没有覆盖目标文件的每一个字节,这些非活动空间是没有定义的。
Section header结构定义如下:
typedef struct{ Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; }Elf32_Shdr; |
其中sh_name指出section的名字,它的值是后面将会讲到的section header string table中的索引,指出一个以null结尾的字符串。Sh_type是类别,sh_flags指示该section在进程执行时的特性。Sh_addr指出若此section在进程的内存映像中出现,则给出开始的虚地址。Sh_offset给出此section在文件中的偏移。其它字段的意义不太常用,在此不细述。
文件的section含有程序和控制信息,系统使用一些特定的section,并有其固定的类型和属性(由sh_type和sh_info指出)。下面介绍几个常用到的section:“.bss”段含有占据程序内存映像的未初始化数据,当程序开始运行时系统对这段数据初始为零,但这个section并不占文件空间。“.data.”和“data1”段包含占据内存映像的初始化数据。“.rodata”和“.rodata1”段含程序映像中的只读数据。“.shstrtab”段含有每个section的名字,由section入口结构中的sh_name索引。“.strtab”段含有表示符号表(symbol table)名字的字符串。“.symtab”段含有文件的符号表,在后文专门介绍。“.text”段包含程序的可执行指令。
6. symbol table
目标文件的符号表包含定位或重定位程序符号定义和引用时所需要的信息。符号表入口结构定义如下:
typedef struct{ Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; Unsigned char st_info; Unsigned char st_other; Elf32_Half st_shndx; }Elf32_Sym; |
其中st_name包含指向符号表字符串表(strtab)中的索引,从而可以获得符号名。St_value指出符号的值,可能是一个绝对值、地址等。St_size指出符号相关的内存大小,比如一个数据结构包含的字节数等。St_info规定了符号的类型和绑定属性,指出这个符号是一个数据名、函数名、section名还是源文件名;并且指出该符号的绑定属性是local、global还是weak。
(责任编辑 尤北)