之前的笔记搬运
ELF文件格式
ELF是 Executable and Linkable Format 的缩写
.o .obj .so 都是ELF文件
.oat是经Android定制的ELF文件
ELF文件按 Executable(可执行)、Linkable(可链接)分为两种内容:
Linking View(链接视图,从编译链接的角度观察ELF文件内容,.o 文件):
ELF header
Program header table optional
Section 1
...
Section n
...
Section header table
Executable View(执行视图,从执行的角度(可执行或动态库文件)观察ELF文件内容,.so 文件):
ELF header
Program header table
Segment 1
Segment 2
...
Section header table optional
ELF文件头结构
64位平台文件头结构:
1 | public class Elf64FileHeader { |
Elf64_Addr 表示程序内的地址,无符号,8字节 ELF32_Addr 4字节
Elf64_Off 表示文件偏移量,无符号,8字节 Elf32_Off 4字节
Elf64_Half Elf32_Half 表示中等大小的整数,无符号 都是2字节, short
Elf64_Word Elf32_Word 无符号整数,都是4字节, 等同于int
32位平台文件头结构:
1 | public class Elf32FileHeader { |
e_type
常见的e_type取值和对应说明:
0 ET_NONE 没有类型
1 ET_REL REL:re-locatable,可重定位文件,如:.obj .o
2 ET_EXEC EXEC:Executable,代表可执行文件
3 ET_DYN DYN:shared object文件,如:.so
4 ET_CORE CORE File,gdb里的core dump 文件属于此类型
OxFF00 ET_LOPROC 和具体的处理器有关,不同处理器定义的e_type取值位于
OxFFFF ET_HIPROC ET_LOPROC和ET_HIPROC之间
e_machine
表示该ELF文件适应于那种CPU平台
标识符 取值 标识符说明
EM_XXX 数字
EM_X86_64 62 AMD x86-64 architecture
e_flags
因平台不同而有差异,取值和解释依赖e_machine
ARM32位平台上 e_machine 为 EM_ARM,40,e_flags为0x02(EF_ARM_HASENTY),表示该ELF文件包含有效e_entry值
ABI Application Binary Interface(二进制接口),ABI和ELF紧密相关
通用ELF文档:Tool Interface Standard(TLS) Portable Formats Specification
通用ABI标准名叫 System V ABI
可查看ELF文件内容的工具:readelf(Linux 系统工具)
Linking View下的 ELF
Linking View中包含若干 Section
Section Header Table 描述的是Section的Header信息
Section Header Table 元素的数据结构
1 | typedef struct { |
几个Section
.shstrtab section
Section Header String Table Section:
字符串存储在 Section Header String Table 中,section名字叫 .shstrtab
类型(sh_type)为SHT_STRTAB(值为3),SHT--Sectoin Header Type,STRTAB--String Tab
字符串在表中按索引查找,以‘\0’作分割符
工具查看: readelf -p [section名 | section索引] xxx.o
.text .bss 等 section
.text :
存储程序的指令(机器指令)
sh_type为SHT_PROGBITS(值为1),Program Bits
sh_flags为SHF_ALLOC(表示该section会分配内存)和SHF_EXECINSTR(该section包含可执行的机器指令)
.bss :
block storage segment,包含一块内存区域,加载到进程空间时设置内存内容为0
sh_type为 SHF_NOBITS(取值为8),由sh_size指定内存大小
sh_flags取值为 SHF_ALLOC和 SHF_WRITE(内存可读写)
int a = 0;可观察到
.data :
与.bss类似,只是内存数据不会初始化为0,得指定具体值
sh_type为SHF_PROGBITS
char c = 'f'; 可观察到
.rodata:
包含只读数据信息,printf里的字符串属于这一类
sh_flags 只能为 SHF_ALLOC
objdump -S -d xxx.o; 可反编译 .text 内容
readelf -x xxx.o; 打印section内容, .bss没有数据无法显示,其他显示值
.symtab
存储符号表(Symbol Table)
sh_type 为 SHT_SYMTAB
符号表主要用于编译链接,参与动态库的加载
(.dynsymtab(类型为SHT_DYNSYM),存储的仅是动态链接需要的符号信息)
符号表元素数据结构:
1 | typedef struct { |
.rel、.rela
和重定位有关,重定位就是将符号的使用之处和符号的定义之处关联起来
重定位表:
以 .rel 开头命名: .rel.text等,跟其他section名称
以 .rela 开头命名: .rela.text等,跟其他section名称
两种表中元素内容细微差别
见上图
重定位表元素数据结构:
1 | typedef struct { |
Execution View下的 ELF
Execution View 必须包含 Program Header Table,与 Section Header Table 对应(Linking View)
PH Table 描述的是 segment 信息
segment 数据结构:
1 | typedef struct { |
ELF文件物理上不包含 segment,segment 是一个或多个section 按一定映射关系组合而来的
p_type、p_flags
Section 到 Segment 映射
区间落在 [p_offset, p_offset+p_filesz] 范围内的 Section 属于同一个 Segment
section 与 segment 在文件上的映射完全匹配(偏移量,位置信息等)
ELF文件核心内容包含在一个个 Section中,Section 类型、内部数据结构、功能等很重要
Section 根据 PH table 的映射关系映射到进程虚拟内存空间中,这时的 Section 就是以 Segment 的样子呈现出来
Linking View 是解析 Section,工具用 readelf
Execution View : Android中用 oatdump 工具解析 oat文件,将文件内容映射(mmap系统调用)到虚拟内存地址上
遍历Segment内容,还是要借助 Section Header Table
动态库加载确定绝对地址的辅助手段
.got section
Global Offset Table
表的每一项存储的是该ELF文件用到的符号(函数或变量)地址
PLT表
Procedure Linkage Table
表项存储的是一段小小的代码,触发符号地址的计算以及跳转到正确的符号地址上
Trampoline Code
.plt
GOT、PLT、Trampoline Code 是动态库加载整个流程顺利进行的关键,待重新深入了解 GOT PLT