ELF文件包含文件头(ELF Header)、程序头表(Program Head Table)、节(Sections)或段(Segments)、节头表(Section Header Table)四个部分。ELF文件提供了两种视图,分别是链接视图和执行视图。其中,节是链接视图的基本单位,在文件进行链接操作时使用;段是执行视图的基本单位,在文件运行时使用。一个段可包含一个或多个节,段和节在文件中没有固定的顺序。以下是elf.h中相关结构体的定义。
1. 文件头结构体(ELF Header)
文件头描述整个文件的组织结构。使用Elf32_Ehdr和Elf64_Ehdr描述。
#define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr;
typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf64_Half e_type; /* Object file type */ Elf64_Half e_machine; /* Architecture */ Elf64_Word e_version; /* Object file version */ Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; /* Processor-specific flags */ Elf64_Half e_ehsize; /* ELF header size in bytes */ Elf64_Half e_phentsize; /* Program header table entry size */ Elf64_Half e_phnum; /* Program header table entry count */ Elf64_Half e_shentsize; /* Section header table entry size */ Elf64_Half e_shnum; /* Section header table entry count */ Elf64_Half e_shstrndx; /* Section header string table index */ } Elf64_Ehdr;
以上,
e_ident 字段前四位为文件标识,一般为“\x7fELF”,通过这四位,可以查看该文件是否为ELF文件。
e_type 字段表示ELF文件的类型,如ET_REL表示可重定位文件,ET_EXEC表示可执行文件,ET_DYN表示共享目标文件。
e_phoff 字段表示程序头表在文件中的偏移,可根据该字段找到程序头表的起始地址。
e_shoff 字段表示节头表在文件中的偏移,可根据该字段找到节头表的起始地址。
e_shstrndx 字段表示字符串表在节头表中的索引,一般为节头表中的最后一个,即e_shstrndx=e_shnum-1。
2. 程序头结构体(Program Header)
程序头表描述文件中各种段的信息。每个程序头的结构体定义如下,使用Elf32_Phdr和Elf64_Phdr描述。
typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ }Elf32_Phdr;
typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ }Elf64_Phdr;
以上,
p_type 字段表示该段的类型,如果值为PT_LOAD,则表示该段是一个可加载到内存中的段,且必须满足 p_memsz ≥ p_filesz ,多出的字节清零,否则该段的内容不能完全加载。在程序头表中,所有PT_LOAD类型的程序头都按照p_vaddr的值升序排列。
3. 节头结构体(Section Header)
节头表描述文件中各种节的信息。每个节头的结构体定义如下,使用Elf32_Shdr和Elf64_Shdr描述。
typedef struct { Elf32_Word sh_name; /* Section name (string tbl index) */ Elf32_Word sh_type; /* Section type */ Elf32_Word sh_flags; /* Section flags */ Elf32_Addr sh_addr; /* Section virtual addr at execution */ Elf32_Off sh_offset; /* Section file offset */ Elf32_Word sh_size; /* Section size in bytes */ Elf32_Word sh_link; /* Link to another section */ Elf32_Word sh_info; /* Additional section information */ Elf32_Word sh_addralign; /* Section alignment */ Elf32_Word sh_entsize; /* Entry size if section holds table */ }Elf32_Shdr;
typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ }Elf64_Shdr;
以上,
sh_name 字段指出节的名字索引,节名字符串以'\0'结尾,统一存储在字符串表中,然后该字段的值就表示对应节名字符串在字符串表中的索引。
4. 字符串表
字符串表存储ELF文件中的节名、符号名等字符串,下面是一个示例,来说明字符串表的存储方式。
假设字符串表如下所示:
则可以理解为:
如示例所示,字符串表索引可以引用节中的任何字节。字符串可以出现多次(11,16);可能存在对子字符串的引用(7,11);一个字符串可能被引用多次;也允许未引用的字符串(索引为22的"xx"字符串)。
下面是从字符串表中获取节名字符串的伪代码示例:
//The target is to get a section's name //1. get sh_name index = section_head_table[i]->sh_name; //2. find string_table string_table = section_head_table[e_shstrndx]->sh_offset; //3. get name name = string_table + index;
补充一个PDF文件链接:Tool Interface Standard (TIS)Executable and Linking Format (ELF) Specification [Version 1.2](全方位带你了解ELF文件结构)
参考资料
1. ELF文件格式解析
2. ELF文件格式分析(ELF文件头)
3. ELF文件-段和程序头
4. ELF文件-节和节头
5. 字符串表