ELF文件格式提供了两种视图,分别是链接视图和执行视图。链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。
尽管图中显示各个组成部分都是有序的,但是实际上除了ELF header之外,其他部分都是没有规定顺序的。
1. ELF Header
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; // Program Header Table的偏移量
ELF32_Off e_shoff; // Section Header Table的偏移量
ELF32_Word e_flags; // 相关处理器的标志
ELF32_Half e_ehsize; // ELF Header结构大小
ELF32_Half e_phentsize; // Program Header Table的表项大小
ELF32_Half e_phnum; // Program Header Table的表项数目
ELF32_Half e_shentsize; // Section Header Table表项大小
ELF32_Half e_shnum; // Section Header Table表项数目
ELF32_Half e_shstrndx; // 字符串表在Section Header Table中的索引
}Elf32_Ehdr;
除了ELF Header之外,其他部分都是没有规定顺序的,所以,这里可以通过ELF Header的e_phoff以及e_shoff字段来确定Program Header Table和Section Header Table的位置。另外,通过e_shstrndx可以确定字符串表在Section Header Table中位置,例如,如果e_shstrndx为25,那么值25表示的是Section Header Table中第25项是字符串表(String Table, .shstrtab)。
2. Section Header Table
一个ELF文件中到底有哪些具体的 Sections,由包含在这个ELF文件中的 Section Header Table决定。在Section Header Table中,针对每一个Section,都有一个相应的条目(entry),用来描述对应的这个Section的相关信息。
typedef struct {
Elf32_Word sh_name; // 对应Section的名字
Elf32_Word sh_type; // 对应Section类型
Elf32_Word sh_flags; // 对应Section在进程中执行的特性(读、写)
Elf32_Addr sh_addr; // 对应Section在内存中的虚拟地址
Elf32_Off sh_offset;// 对应Section在文件中的偏移
Elf32_Word sh_size; // 对应Section的大小
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
需要注意的是,sh_name值实际上是String Table(.shstrtab)中的索引,也就是说sh_name对应的并不是一个字符串,而且表示的一个索引值,通过这个索引值,我们可以在String Table中找到对应的字符串,而String table(.shstrtab)中存储着所有Section的名字。
3. Section
分析一些so文件中重要的Section,包括符号表、重定位表、GOT表等。
-Symbol Table(.dynsym)
符号表记录了该文件中的所有符号,所谓的符号就是经过修饰了的函数名或者变量名。
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跟上面Section Header Table的sh_name一样,表示的是符号名在字符串表中的索引。
-String Table(.dynstr)
.dynstr和.shstrtab结构完全相同,不过一个存储的是符号名称的字符串,而另一个是Section名称的字符串。
-重定位表
重定位是将符号引用与符号定义进行连接的过程
typedef struct {
Elf32_Addr r_offset; // 符号在got表的偏移地址
Elf32_Word r_info; // 进行重定位的符号在符号表索引
} Elf32_Rel;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Word r_addend;
} Elf32_Rela;
-全局偏移表(GOT)
全局偏移表(GOT)用来将位置独立的地址计算重定向到绝对位置。
-过程链接表(PLT)
过程链接表(PLT)能够把位置独立的函数调用重定向到绝对位置。
4. Program Header Table
程序头部(Program Header)描述与程序执行直接相关的目标文件结构信息。
typedef struct {
Elf32_Word p_type; // 此数组元素描述的段的类型
Elf32_Off p_offset; // 在文件中的偏移
Elf32_Addr p_vaddr; // 在内存中的虚拟地址
Elf32_Addr p_paddr; // 在内存中的物理地址信息。
Elf32_Word p_filesz; // 大小
Elf32_Word p_memsz; //在内存映像中占用的字节数。可以为0。
Elf32_Word p_flags; //相关的标志。
Elf32_Word p_align; //在文件中和内存中如何对齐。
} Elf32_phdr;
参考文章:
ELF文件格式解析
linux第三次实践:ELF文件格式分析