0%

2024-03-21-ELF文件机构

Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; //魔术值
Elf64_Half e_type; //类型 3为共享库
Elf64_Half e_machine; // 体系结构类型 183为aarch64
Elf64_Word e_version; // 版本信息
Elf64_Addr e_entry; // 程序入口地址 0x0000000000003FFC
Elf64_Off e_phoff; // program header 偏移
Elf64_Off e_shoff; // section header 偏移
Elf64_Word e_flags; //
Elf64_Half e_ehsize; // header size
Elf64_Half e_phentsize; // program header entry结构 size
Elf64_Half e_phnum; // program header size
Elf64_Half e_shentsize; // section header entry结构 size
Elf64_Half e_shnum; // section header size
Elf64_Half e_shstrndx; // section中string table index
} Elf64_Ehdr;

image.png

e_machine 取值
https://codebrowser.dev/glibc/glibc/elf/elf.h.html

Program Header

1
2
3
4
5
6
7
8
9
10
typedef struct elf64_phdr {
Elf64_Word p_type; // 类型
Elf64_Word p_flags; // 该字段给出了与段相关的标记
Elf64_Off p_offset; // 该字段给出了该段内容开始的偏移,可能为0
Elf64_Addr p_vaddr; // 该字段给出了该段第一个字节在内存中的虚拟地址,可能没有地址为0
Elf64_Addr p_paddr; // 该字段仅用于物理地址寻址相关的系统中
Elf64_Xword p_filesz; // 该字段给出了文件镜像中该段的大小,可能为 0
Elf64_Xword p_memsz; // 该字段给出了内存镜像中该段的大小,可能为 0
Elf64_Xword p_align; // 对齐
} Elf64_Phdr;
  • 该段开始位置为基地址 + p_offset,长度为 p_memsz

image.png

p_flags

image.png

p_type

取值 代表 含义
00 PT_NULL 此数组元素未用。结构中其他成员都是未定义的。
01 PT_LOAD 此数组元素给出一个可加载的段,段的大小由 p_filesz 和 p_memsz 描述。文件中的字节被映射到内存段开始处。如果 p_memsz 大于 p_filesz,“剩余”的字节要清零。p_filesz 不能大于 p_memsz。可加载的段在程序头部表格中根据 p_vaddr 成员按升序排列。
02 PT_DYNAMIC 数组元素给出动态链接信息。
03 PT_INTERP 数组元素给出一个 NULL 结尾的字符串的位置和长度,该字符串将被当作解释器调用。这种段类型仅对与可执行文件有意义(尽管也可能在共享目标文件上发生)。在一个文件中不能出现一次以上。如果存在这种类型的段,它必须在所有可加载段项目的前面。
04 PT_NOTE 此数组元素给出附加信息的位置和大小。
05 PT_SHLIB 此段类型被保留,不过语义未指定。包含这种类型的段的程序与 ABI不符。
06 PT_PHDR 此类型的数组元素如果存在,则给出了程序头部表自身的大小和位置,既包括在文件中也包括在内存中的信息。此类型的段在文件中不能出现一次以上。并且只有程序头部表是程序的内存映像的一部分时才起作用。如果存在此类型段,则必须在所有可加载段项目的前面。
0x70000000 PT_LOPROC 此范围的类型保留给处理器专用语义。
0x7fffffff PT_HIPROC 此范围的类型保留给处理器专用语义。

Section Header

1
2
3
4
5
6
7
8
9
10
11
12
typedef 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;
} Elf64_Shdr;
成员 说明
sh_name 节名称,是节区头字符串表节区中(Section Header String Table Section)的索引,因此该字段实际是一个数值。在字符串表中的具体内容是以 NULL 结尾的字符串。
sh_type 根据节的内容和语义进行分类,具体的类型下面会介绍
sh_flags 每一比特代表不同的标志,描述节是否可写,可执行,需要分配内存等属性。
sh_addr 如果节区将出现在进程的内存映像中,此成员给出节区的第一个字节应该在进程镜像中的位置。否则,此字段为 0
sh_offset 给出节区的第一个字节与文件开始处之间的偏移。SHT_NOBITS 类型的节区不占用文件的空间,因此其 sh_offset 成员给出的是概念性的偏移。
sh_size 此成员给出节区的字节大小。除非节区的类型是 SHT_NOBITS ,否则该节占用文件中的 sh_size 字节。类型为 SHT_NOBITS 的节区长度可能非零,不过却不占用文件中的空间。
sh_link 此成员给出节区头部表索引链接,其具体的解释依赖于节区类型
sh_info 此成员给出附加信息,其解释依赖于节区类型。
sh_addralign 某些节区的地址需要对齐。例如,如果一个节区有一个 doubleword 类型的变量,那么系统必须保证整个节区按双字对齐。也就是说sh_addr%sh_addralign = 0。 目前它仅允许为 0,以及 2 的正整数幂数。 0 和 1 表示没有对齐约束
sh_entsize 某些节区中存在具有固定大小的表项的表,如符号表。对于这类节区,该成员给出每个表项的字节大小。反之,此成员取值为 0
  • header中e_shstrndx记录了.shstrtab的索引位置,sh_name的字符串名为 :基地址 + shstrtab.sh_offset + section_h.sh_name
  • section_h.sh_offset + section_h.sh_size = 下一个section的起始位置
  • section_h.sh_size / section_h.sh_entsize = entry count (symtab count 和dynsym count)

image.png

sh_flags

image.png

sh_type

名称 取值 说明
SHT_NULL 0 此值标志节区头部是非活动的,没有对应的节区。此节区头部中的其他成员取值无意义。
SHT_PROGBITS 1 此节区包含程序定义的信息,其格式和含义都由程序来解释。
SHT_SYMTAB 2 此节区包含一个符号表。目前目标文件对每种类型的节区都只能包含一个,不过这个限制将来可能发生变化。一般,SHT_SYMTAB 节区提供用于链接编辑(指 ld 而言)的符号,尽管也可用来实现动态链接。
SHT_STRTAB 3 此节区包含字符串表。目标文件可能包含多个字符串表节区。
SHT_RELA 4 此节区包含重定位表项,其中可能会有补齐内容(addend),例如 32 位目标文件中的 Elf32_Rela 类型。目标文件可能拥有多个重定位节区。
SHT_HASH 5 此节区包含符号哈希表。所有参与动态链接的目标都必须包含一个符号哈希表。目前,一个目标文件只能包含一个哈希表,不过此限制将来可能会解除。
SHT_DYNAMIC 6 此节区包含动态链接的信息。目前一个目标文件中只能包含一个动态节区,将来可能会取消这一限制。
SHT_NOTE 7 此节区包含以某种方式来标记文件的信息。
SHT_NOBITS 8 这种类型的节区不占用文件中的空间,其他方面和 SHT_PROGBITS 相似。尽管此节区不包含任何字节,成员sh_offset 中还是会包含概念性的文件偏移
SHT_REL 9 此节区包含重定位表项,其中没有补齐(addends),例如 32 位目标文件中的 Elf32_rel 类型。目标文件中可以拥有多个重定位节区。
SHT_SHLIB 10 此节区被保留,不过其语义是未规定的。包含此类型节区的程序与 ABI 不兼容。
SHT_DYNSYM 11 作为一个完整的符号表,它可能包含很多对动态链接而言不必要的符号。因此,目标文件也可以包含一个 SHT_DYNSYM 节区,其中保存动态链接符号的一个最小集合,以节省空间。
SHT_LOPROC 0X70000000 这一段(包括两个边界),是保留给处理器专用语义的。
SHT_HIPROC 0X7FFFFFFF 这一段(包括两个边界),是保留给处理器专用语义的。
SHT_LOUSER 0X80000000 此值给出保留给应用程序的索引下界。
SHT_HIUSER 0X8FFFFFFF 此值给出保留给应用程序的索引上界。

sh_name

sh_name sh_type 说明
.text SHT_PROGBITS 代码段,包含程序的可执行指令
.data SHT_PROGBITS 包含初始化了的数据,将出现在程序的内存映像中
.bss SHT_NOBITS 未初始化数据
.rodata SHT_PROGBITS 包含只读数据
.comment SHT_PROGBITS 包含版本控制信息
.dynsym SHT_DYNSYM 此节区包含了动态链接符号表
.shstrtab SHT_STRTAB 存放section名,字符串表。Section
Header String Table
.strtab SHT_STRTAB 字符串表
.symtab SHT_SYMTAB 符号表
.rel.text SHT_REL 告诉链接器,哪些地方需要重定向。

符号表

1
2
3
4
5
6
7
8
typedef struct elf64_sym {
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
  • st_name 符号名称,给出的是一个在符号名称表(.dynstr)中的索引
  • st_info 用于标示此符号的属性,占一个字节(2个字),两个标示位,第一个标示位(低四位)标志作用域,第二个标示位(高四位)标示符号类型
  • st_other 固定值为0。
  • **st_shndx **每个符号表项都以和其他节区间的关系的方式给出定义。此成员给出相关的节区头部表索引。某些索引具有特殊含义。
  • st_value 一般都是函数地址,或者是一个常量值
  • st_size 从 st_value 地址开始,共占的长度大小

    c++filt 函数解析

其他

  • dynsym表中的符号是动态链接的符号

    • 如果有地址,则可以被外部调用
    • 如果没有地址,则需要依赖外部
  • libart.so 中带 symtab符号表,如果想调用没有导出的符号,可以查询symtab找到符号的逻辑地址,再加上maps中libart.so的基地址的地址,就是程序中符号的虚拟地址了。

  • dynstr,strtab,shstrtab的区别

    • dynstr 动态字符串表,主要给dynsym表中使用
    • strtab 字符串表,主要给symtab表使用
    • shstrtab 给 section header table中使用