一、 节头表(section header table)
在目标文件中可以包含很多“节”(section),所有这些“节”都登记在一张称为 “节头表”(section header table)的数组里。通过每一个表项可以定位到对应的节。
每一个表项结构如下:
struct Elf64_Shdr {
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
};
1. sh_name
本节的名字。整个名字的字符串并不存储在这里,它仅是一个索引号,指向 “字符串表”节中的某个位置,那里存储了一个以’\0’结尾的字符串。
在第一篇文章中的elf header中的 e_shtrndx 可以知道 节头表里面字符串表的索引。
从s_offset的0x40F5偏移 s_name_off 也就是 0x19 开始到第一个 0 结尾取出内容刚好是 .text;
2. sh_type
本节的类型。
#define SHT_NULL 0 /* 节头表条目未使用 */
#define SHT_PROGBITS 1 /* 程序数据 */
#define SHT_SYMTAB 2 /* 符号表 */
#define SHT_STRTAB 3 /* 字符串表 */
#define SHT_RELA 4 /* 带加数的重定向条目 */
#define SHT_HASH 5 /* 符号哈希表 */
#define SHT_DYNAMIC 6 /* 动态链接信息 */
#define SHT_NOTE 7 /* Notes */
#define SHT_NOBITS 8 /* 没有数据的程序空间(bss) */
#define SHT_REL 9 /* 重定位条目,无加数 */
#define SHT_SHLIB 10 /* Reserved */
#define SHT_DYNSYM 11 /* 动态链接符号表 */
#define SHT_INIT_ARRAY 14 /* 构造函数数组 */
#define SHT_FINI_ARRAY 15 /* 析构函数数组 */
#define SHT_PREINIT_ARRAY 16 /* 预构造函数数组 */
#define SHT_GROUP 17 /* 节组 */
#define SHT_SYMTAB_SHNDX 18 /* 扩展节索引 */
#define SHT_NUM 19 /* 已定义类型的数量 */
#define SHT_LOOS 0x60000000 /* 启动特定于操作系统 */
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* 对象属性 */
#define SHT_GNU_HASH 0x6ffffff6 /* GNU风格的哈希表 */
#define SHT_GNU_LIBLIST 0x6ffffff7 /* 预链接库列表 */
#define SHT_CHECKSUM 0x6ffffff8 /* DSO内容的校验和 */
#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
#define SHT_SUNW_move 0x6ffffffa
#define SHT_SUNW_COMDAT 0x6ffffffb
#define SHT_SUNW_syminfo 0x6ffffffc
#define SHT_GNU_verdef 0x6ffffffd /* 版本定义节 */
#define SHT_GNU_verneed 0x6ffffffe /* 版本需要节 */
#define SHT_GNU_versym 0x6fffffff /* 版本符号表 */
#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
#define SHT_HIOS 0x6fffffff /* End OS-specific type */
#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
#define SHT_LOUSER 0x80000000 /* Start of application-specific */
#define SHT_HIUSER 0x8fffffff /* End of application-specific */
3. sh_flags
本节的一些属性,由一系列标志比特位组成,各个比特定义了节的不同属性,当某种属性被设置时,相应的标志位被设为 1,反之则设为 0。未定义的标志 位被全部置 0。
名称 | 值 | |
SHF_WRITE | 1 | 可写 |
SHF_ALLOC | 2 | 在执行过程中占用内存 |
SHF_ALLOC_WRITE | 3 | 占用内存,可写 |
SHF_EXEC | 4 | 可执行 |
SHF_WRITE_EXEC | 5 | 可写,可执行 |
SHF_ALLOC_EXEC | 6 | 占用内存,可执行 |
4. sh_addr
如果本节的内容需要映射到进程空间中去,此成员指定映射的起始地址;如果不需要映射,此值为 0。
5. sh_offset
相对于文件开头的偏移量。单位是字节。如果该节的类型为 SHT_NOBITS 的话,表明这一节的内容是空的,节并不占用实际的空间,这时 sh_offset 只代表一个逻辑上的位置概念,并不代表实际的内容。
6. sh_size
指明节的大小,单位是字节。如果该节的类型为 SHT_NOBITS,此值仍然可能为非零,但没有实际的意义。
7. sh_link
节头表索引链接,其解释依赖于节类型。
8. sh_info
此成员含有此节的附加信息,根据节的类型不同,本成员的意义也有所不同。
9. sh_addralign
一些节具有地址对齐约束。例如,如果某节包含双字,则系统必须确保整个节双字对齐。在此情况下,sh_addr 的值在以 sh_addralign 的值为模数进行取模时,同余数必须等于 0。当前,仅允许使用 0 和 2 的正整数幂。值 0 和 1 表示节没有对齐约束。
10. sh_entsize
一些节包含固定大小的项的表,如符号表。对于这样的节,此成员会指定每一项的大小(以字节为单位)。如果节不包含固定大小的项的表,则此成员值为零。
二、 特殊节
.note
供应商或系统工程师可能需要使用特殊信息标记目标文件,以便其他程序可根据此信息检查一致性或兼容性。为此,可使用 SHT_NOTE 类型的节和 PT_NOTE 类型的程序头元素。
.hash
符号散列表。散列表由用于符号表访问的 Elf32_Word 或 Elf64_Word 目标文件组成。SHT_HASH 节提供了此散列表。与散列关联的符号表在散列表节头的 sh_link 项中指定。
.dynsym
动态链接符号表。
.dynstr
进行动态链接所需的字符串,通常是表示与符号表各项关联的名称的字符串。
.rela
.重定位是连接符号引用与符号定义的过程。例如,程序调用函数时,关联的调用指令必须在执行时将控制权转移到正确的目标地址。可重定位文件必须包含说明如何修改其节内容的信息。通过此信息,可执行文件和共享目标文件可包含进程的程序映像的正确信息。重定位项即是这些数据。
.plt
此节包含函数连接表
.relname 和.relaname
这两个节含有重定位信息。如果此节被包含在某个可装载的段中,那么本节的属性中应置 SHF_ALLOC 标志位,否则不置此标志。注意,这两个节的名字 中”name”是可替换的部分,执照惯例,对哪一节做重定位就把”name”换成哪一节的名字。比如,.plt 节的重定位节的名字将是.rel.plt 或.rela.plt
.text
程序的文本或可执行指令。
.rodata
通常构成进程映像中的非可写段的只读数据。
.eh_frame_hdr 和 .eh_frame
用于展开栈的调用帧信息。
.init_array
函数指针数组,用于构成包含此节的可执行文件或共享目标文件的单个初始化数组。
.fini_array
函数指针数组,用于构成包含此节的可执行文件或共享目标文件的单个终止数组。
.dynamic
本节包含动态连接信息,并且可能有 SHF_ALLOC 和 SHF_WRITE 等属性。是否具有 SHF_WRITE 属性取决于操作系统和处理器。
.got
此节包含全局偏移量表。
.data
构成程序的内存映像的已初始化数据。
.comment
注释信息,通常由编译系统的组件提供。
.shstrtab
本节是“节名字表”,含有所有其它节的名字。
.bss
本节中包含目标文件中未初始化的全局变量。一般情况下,可执行程序在开始运行的时候,系统会把这一段内容清零。但是,在运行期间的 bss 段是由系统初始化而成的,在目标文件中.bss 节并不包含任何内容,其长度为 0,