ELF头文件
4种ELF文件类型
ELF文件类型 | 说明 | 实例 |
---|---|---|
Relocatable File | 包含例代码和数据,可以被链接成可执行文件或共享目标文件 | Linux下的.o文件 |
Executable File | 包含可以直接执行的程序,ELF可执行文件,一般没有扩展名 | /bin/bash文件 |
Shared Object File | 包含代码和数据,和其他目标文件链接成新的目标文件,和可执行文件链接作为进程映像的一部分来允许 | Linux下的.so文件 |
Core Dump File | 进程意外终止时可以产生的文件,存储着该进程的内存空间中的内容等信息 | Linux下的core dump |
ELF文件构成
两种视图
section和segment的区别:
- section称为节,是指在汇编源码中经由关键字section或segment修饰、逻辑划分的指令或数据区域。
- segment称为段,是根据目标文件中属性相同的多个section合并后的section集合,这个集合称为segment。我们平时所说的可执行程序内存空间中的代码段和数据段就是指的segment。
- section主要提供给Linker使用, 而segment提供给Loader用。Linker需要关心.text、.rel.text、.data、.rodata等,因为Linker需要做relocation,而Loader只需要知道Read/Write/Execute的属性
- executable的ELF文件可以没有section,但必须有segment。ELF文件中间部分是共用的(也就是代码段、数据段等),如shared objects就可以同时拥有Program header table和Section Header Table,这样load完后还可以relocate。
- 这样设定之后,使得Loader需要做的工作大大减少了,一定程度上提高了程序加载的效率。
结构图
注意点
- 除了 ELF 头部表以外,其他节区和段都没有规定的顺序
- 目标文件中的每个节区都有对应的节区头部描述它,反过来,有节区头部不意味着有节区
- 每个节区占用文件中一个连续字节区域(这个区域可能长度为 0)。
- 文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。
- 目标文件中可能包含非活动空间(INACTIVE SPACE)。这些区域不属于任何头部和节区,其内容未指定。
- 以“.”开头的节区名称是系统保留的。应用程序可以使用没有前缀的节区名称,以避免与系统节区冲突。
- 目标文件中也可以包含多个名字相同的节区。
- Section和Segment的区别和联系
可执行文件中,一个program header描述的内容称为一个段(segment)。Segment包含一个或者多个section - 可执行程序中的几个段:
名称 | 内容 |
---|---|
代码段 | 可执行代码、字符串常量 |
数据段 | 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据 |
BSS段 | 未初始化全局变量,未初始化全局静态变量 |
栈 | 局部变量、函数参数 |
堆 | 动态内存分配 |
section类型
名称 | 类型 | 属性 | 含义 |
---|---|---|---|
.bss | SHT_NOBITS | SHF_ALLOC SHF_WRITE | 包含将出现在程序的内存映像中的为初始化数据。根据定义,当程序开始执行,系统将把这些数据初始化为 0。此节区不占用文件空间。 |
.data | SHT_PROGBITS | (无) | 包含版本控制信息。 |
.data1 | SHT_PROGBITS | SHF_ALLOC SHF_WRITE | 这些节区包含初始化了的数据,将出现在程序的内存映像中。 |
.debug | SHT_PROGBITS | (无) | 此节区包含用于符号调试的信息。 |
.dynamic | SHT_DYNAMIC | 此节区包含动态链接信息。节区的属性将包含 SHF_ALLOC 位。是否 SHF_WRITE 位被设置取决于处理器。 | |
.dynstr | SHT_STRTAB | SHF_ALLOC | 此节区包含用于动态链接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。 |
.dynsym | SHT_DYNSYM | SHF_ALLOC | 此节区包含了动态链接符号表。 |
.fini | SHT_PROGBITS | SHF_ALLOCSHF_EXECINSTR | 此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时,系统将安排执行这里的代码。 |
.got | SHT_PROGBITS | 此节区包含全局变量偏移表。存放着调用外部函数的实际地址(第一次存放的是PLT中的指令,PLT执行完之后会把计算得到的实际值再存到GOT中)。 | |
.got.plt | ELF将GOT拆分成两个表 .got和.got.plt,前者用来保存全局变量引用的地址,后者用来保存函数引用的地址。 | ||
.hash | SHT_HASH | SHF_ALLOC | 此节区包含了一个符号哈希表. |
.init | SHT_PROGBITS | SHF_ALLOCSHF_EXECINSTR | 此节区包含了可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在SHF_EXECINSTR 开始调用主程序入口之前(通常指 C 语言的 main 函数)执行这些代码。 |
.interp | SHT_PROGBITS | 此节区包含程序解释器的路径名。如果程序包含一个可加载的段,段中包含此节区,那么节区的属性将包含 SHF_ALLOC 位,否则该位为 0。 | |
.line | SHT_PROGBITS | 此节区包含符号调试的行号信息,其中描述了源程序与机器指令之间的对应关系。其内容是未定义的。 | |
.note | SHT_NOTE | 此节区中包含注释信息,有独立的格式。 | |
.plt | SHT_PROGBITS | 此节区包含过程链接表(procedure linkage table)。所有对外部函数的调用都经过PLT再到GOT的一个调用过程。 | |
.relname | SHT_REL | 这些节区中包含了重定位信息。如果文件中包含可加载的段,段中有重定位内容,节区的属性将包含 SHF_ALLOC 位,否则该位 置 0。传统上 name 根据重定位所适用的节区给定。例如 .text 节区的重定位节区名字将是:.rel.text 或者 .rela.text。 | |
.rel.dyn | 重定位的地方在.got段内,主要是针对外部数据变量符号。不支持延迟重定位(Lazy),通常是在so文件执行时就在.init段中进行重定位操作。 | ||
.rel.plt
|
重定位的地方在.got.plt段内, 主要是针对外部函数符号。一般是函数首次被调用时候重定位。 | ||
.rela name | SHT_RELA | ||
.rodata | SHT_PROGBITS | SHF_ALLOC | 这些节区包含只读数据,这些数据通常参与进程映像的不可写段。 |
.rodata1 | SHT_PROGBITS | SHF_ALLOC | |
.shstrtab | SHT_STRTAB | 此节区包含节区名称。 | |
.strtab | SHT_STRTAB | 此节区包含字符串,通常是代表与符号表项相关的名称。如果文件拥有一个可加载的段,段中包含符号串表,节区的属性将包含 SHF_ALLOC 位,否则该位为 0。 | |
.symtab | SHT_SYMTAB | 此节区包含一个符号表。如果文件中包含一个可加载的段,并且该段中包含符号表,那么节区的属性中包含SHF_ALLOC 位,否则该位置为 0。 | |
.text | SHT_PROGBITS | 此节区包含程序的可执行指令。 |
segment类型
程序段类型 | 取值 | 说明 |
PT_NULL | 0 | 此数组元素未用。结构中其他成员都是未定义的。 |
PT_LOAD | 1 | 此数组元素给出一个可加载的段,段的大小由 p_filesz 和 p_memsz描述。文件中的字节被映射到内存段开始处。如果 p_memsz 大于p_filesz,“剩余”的字节要清零。p_filesz 不能大于 p_memsz。可加载的段在程序头部表格中根据 p_vaddr 成员按升序排列。 |
PT_DYNAMIC | 2 | 数组元素给出动态链接信息。Dynamic Segment 是很重要的一个程序头,里面存储着函数名、使用过的动态库名、重定位表、函数代码偏移等重要信息,不过不是直接记录,而是通过一定的方法查询得到,这个查询过程是elf设计中巧妙且关键的核心所在。 |
PT_INTERP | 3 | 该记录的是链接器linker的路径.数组元素给出一个 NULL 结尾的字符串的位置和长度,该字符串将被当作解释器调用。这种段类型仅对与可执行文件有意义(尽管也可能在共享目标文件上发生)。在一个文件中不能出现一次以上。如果存在这种类型的段,它必须在所有可加载段项目的前面。 |
PT_NOTE | 4 | 此数组元素给出附加信息的位置和大小。 |
PT_SHLIB | 5 | 此段类型被保留,不过语义未指定。包含这种类型的段的程序与 ABI 不符。 |
PT_PHDR | 6 | 此类型的数组元素如果存在,则给出了程序头部表自身的大小和位置,既包括在文件中也包括在内存中的信息。此类型的段在文件中不能出现一次以上。并且只有程序头部表是程序的内存映像的一部分时才起作用。如果存在此类型段,则必须在所有可加载段项目的前面。 |
PT_LOPROC | 0x70000000 | 此范围的类型保留给处理器专用语义。 |
PT_HIPROC | 0x7ffffffff |