ELF文件格式

Chiu Lv4

ELF(Executable and Linkable Format)是一种常见的可执行文件和可链接文件格式,主要用于Linux和类Unix系统。ELF 文件可以包含不同的类型,常见的 ELF 文件类型包括:

  • 可执行文件(ET_EXEC):这种类型的 ELF 文件是可直接执行的程序,可以在操作系统上运行。
  • 共享目标文件(ET_DYN):这种类型的 ELF 文件是可被动态链接的共享库,可以在运行时与其他程序动态链接。该类型文件后缀名为 .so
  • 可重定位文件(ET_REL):这种类型的 ELF 文件是编译器生成的目标文件,通常用于将多个目标文件链接到一个可执行文件或共享库中。该类型文件后缀名为 .o ,静态链接库(.a)也可以归为这一类。
  • 核心转储文件(ET_CORE):这种类型的 ELF 文件是操作系统在程序崩溃或发生错误时生成的核心转储文件,用于调试和分析程序崩溃的原因。

ELF 文件结构及相关常数被定义在 /usr/include/elf.h 里,因为 ELF 文件在各种平台下都通用,ELF文件有 32 位版本和 64 位版本。32 位版本与 64 位版本的 ELF 文件的格式基本是一样的(部分结构体为了优化对齐后大小调整了成员的顺序),只不过有些成员的大小不一样。

elf.h 使用 typedef 定义了一套自己的变量体系:

自定义类型 描述 原始类型 长度(字节)
Elf32_Addr 32 位版本程序地址 uint32_t 4
Elf32_Half 32 位版本的无符号短整型 uint16_t 2
Elf32_Off 32 位版本的偏移地址 uint32_t 4
Elf32_Sword 32 位版本有符号整型 uint32_t 4
Elf32_Word 32 位版本无符号整型 int32_t 4
Elf64_Addr 64 位版本程序地址 uint64_t 8
Elf64_Half 64 位版本的无符号短整型 uint16_t 2
Elf64_Off 64 位版本的偏移地址 uint64_t 8
Elf64_Sword 64 位版本有符号整型 uint32_t 4
Elf64_Word 64 位版本无符号整型 int32_t 4

ELF 主要管理结构为文件头,程序头表(可重定位文件没有)和节表,其他部分有一个个节组成,多个属性相同的节构成一个段。对于节的介绍这里按照静态链接相关和动态链接相关分别介绍。图片描述

文件头

我们这里以 32 位版本的文件头结构 Elf32_Ehdr 作为例子来描述,它的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* The ELF file header.  This appears at the start of every ELF file.  */

#define EI_NIDENT (16)

typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
  • e_ident:ELF 文件的魔数和其他信息。
    • 前 4 字节为 ELFMAGx7fELF
    • 第 5 字节为 ELF 文件类型,值为 ELFCLASS32(1) 代表 32 位,值为 ELFCLASS64(2) 代表 64 位。
    • 第 6 字节为 ELF 的字节序,0 为无效格式,1 为小端格式,2 为大端格式。
    • 第 7 字节为 ELF 版本,一般为 1 ,即 1.2 版本。
    • 后面 9 字节没有定义一般填 0 ,有些平台会使用这 9 个字节作为扩展标志。
  • e_type:表示ELF文件类型,如可执行文件、共享对象文件(.so)、可重定位文件(.o)等。
  • e_machine:表示目标体系结构,即程序的目标平台,如 x86、ARM 等。相关常量以 EM_ 开头。
  • e_version:ELF 文件版本号,一般为常数 1 。
  • e_entry:表示程序入口点虚拟地址。操作系统加载完程序后从这个地址开始执行进程的命令。可重定位文件一般没有入口地址,则这个值为 0 。
  • e_phoff:表示程序头表的文件偏移量。
  • e_shoff:表示节表的文件偏移量。
  • e_flags:表示处理器特定标志。
  • e_ehsize:表示 ELF 文件头的大小。
  • e_phentsize:表示程序头表中每个表项的大小。
  • e_phnum:表示程序头表中表项的数量。
  • e_shentsize:表示节表中每个表项的大小。
  • e_shnum:表示节表中表项的数量。
  • e_shstrndx表示节表中字符串表的索引。

程序头表

ELF 可执行文件中有一个专门的数据结构叫做程序头表(Program Header Table)用来保存注意不是节)的信息。因为 ELF 目标文件不需要被装载,所以它没有程序头表,而 ELF 的可执行文件共享库文件都有程序头表。

程序头表是由 Elf*_Phdr 组成的数组,用于描述 ELF 文件中每个节的属性和信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* Program segment header.  */

typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
  • p_type:段的类型,例如可执行段、数据段等。
  • p_offset:段在文件中的偏移量。
  • p_vaddr:段在虚拟内存中的起始地址。
  • p_paddr:段在物理内存中的起始地址。因为 ELF 还没装载不知道物理地址,所以作为保留字段。通常和 p_vaddr 的值是一样的。
  • p_filesz:段在文件中的大小。
  • p_memsz:段在内存中的大小。
  • p_flags:段的标志,例如可读、可写、可执行等。
  • p_align:段在文件和内存中的对齐方式。段的的加载地址要能被 2������2palign 整除。

ELF文件里面定义一个固定长度的 Elf*_Shdr 结构体数组用来存放相关信息,与 PE 文件的节表相似。

在 ELF 文件中,(Segment)和(Section)是两个不同的概念,它们在文件结构中具有不同的作用和目的。

段(Segment)是一种逻辑上的组织单位,它定义了可执行文件或共享库在内存中的一个连续区域。每个段都有自己的虚拟地址空间,可以包含多个节。常见的段类型包括代码段(.text),数据段(.data.bss),只读数据段(.rodata)等。段在加载和执行时被操作系统用来管理内存,设置内存保护属性以及指定虚拟地址空间的起始地址和大小。

节(Section)是一种更细粒度的组织单位,它包含了文件中的特定类型的数据或代码。每个节都有自己的名字、类型和内容。常见的节类型包括代码节(.text),数据节(.data.bss),只读数据节(.rodata),符号表节(.symtab),字符串表节(.strtab)等。节不直接参与内存的加载和执行,而是用于链接器(Linker)和调试器(Debugger)等工具对文件进行处理和分析。

通俗的讲,在装载程序的时候为了节省内存会将 ELF 文件中属性相同的节(Section)合并成在一个段(Segment)加载到内存中。

段和节之间存在对应关系和映射关系:

  • 一个段可以包含多个节,这些节的内容和属性都属于该段。
  • 段提供了对应于虚拟内存的逻辑映射,而节则提供了对应于文件的逻辑映射。
  • 段的加载和执行涉及内存管理和地址映射,而节则用于链接和调试过程中的符号解析、重定位等操作。

其中 Elf32_Shdr 定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Section header.  */

typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
  • sh_name:表示节的名称在字符串表中的索引。字符串表节存储了所有节的名称,sh_name 指定了节的名称在字符串表中的位置。

  • sh_type:表示节的类型,指定了节的用途和属性。常见的类型包括代码段(SHT_PROGBITS(1))、数据段(SHT_PROGBITS(1))、符号表(SHT_SYMTAB(2))、字符串表(SHT_STRTAB(3))等。

  • sh_flags:表示节的标志,用于描述节的特性和属性。标志的具体含义取决于节的类型和上下文。

  • sh_addr:表示节的虚拟地址,只在可执行文件中有意义。对于可执行文件,sh_addr 指定了节在内存中的加载地址,如果该节不可被加载,则该值为 0 。

  • sh_offset:表示节在文件中的偏移量,指定了节在文件中的位置。对于 bss 段来说该值没有意义。

  • sh_size:表示节的大小,指定了节所占据的字节数。

  • sh_link:表示链接到的其他节的索引,用于建立节之间的关联关系,具体含义依赖于节的类型。

  • sh_info:附加信息,具体含义依赖于节的类型。

  • sh_addralign:表示节的地址对齐要求,指定了节在内存中的对齐方式。即 sh_addr 需要满足
    $$
    sh_addr \bmod 2^{\text{sh_addralign}}
    $$
    ​ 如果 sh_addralign 为 0 或 1 表示该段没有对齐要求。

  • sh_entsize:表示节中每个项的大小,如果该字段为 0 说明节中不包含固定大小的项。

ELF 中常见的节如下:

  • .text:代码段(Code Section),用于存储程序的可执行指令。
  • .rodata:只读数据段(Read-Only Data Section),用于存储只读的常量数据,例如字符串常量。
  • .data:数据段(Data Section),用于存储已初始化的全局变量和静态变量。
  • .bss:未初始化的数据段(Block Started by Symbol),用于存储未初始化的全局变量和静态变量。它不占用实际的文件空间,而是在运行时由系统自动初始化为零。
  • .symtab:符号表节(Symbol Table Section),用于存储程序的符号表信息,包括函数、变量和其他符号的名称、类型和地址等。
  • .strtab:字符串表节(String Table Section),用于存储字符串数据,如节名称、符号名称等。字符串表节被多个其他节引用,通过偏移量和索引来访问具体的字符串。
  • .rel.text.rela.text:代码重定位节(Relocation Section),用于存储代码段中的重定位信息,以便在链接时修正代码中的符号引用。
  • .rel.data.rela.data:数据重定位节(Relocation Section),用于存储数据段中的重定位信息,以便在链接时修正数据段中的符号引用。
  • .dynamic:动态节(Dynamic Section),用于存储程序的动态链接信息,包括动态链接器需要的重定位表、共享对象的名称、版本信息等。
  • .note:注释节(Note Section),用于存储与程序或库相关的注释或调试信息。

静态链接相关

注意:静态链接相关只在可重定位文件中存在。比如可执行文件,如果不开启 PIE 加载地址固定,不需要对自身进行重定位,而开启 PIE 后为地址无关代码,也不需要对自身进行重定位。因此不需要静态链接也就丢弃了静态链接相关的节。

符号表(.symtab)

注意:符号表除了静态链接外没有用,但是程序为了方便调试会保留符号表,我们可以通过 strip + 程序名 的方式将符号表去除,这就是为什么有的 pwn 题的附件没有函数和变量名而有的却有。

ELF 文件中的符号表往往是文件中的一个段,段名一般叫 .symtab 。符号表是一个 Elf*_Sym 结构(32 位 ELF 文件)的数组,每个 Elf*_Sym 结构对应一个符号。

1
2
3
4
5
6
7
8
9
10
11
/* Symbol table entry.  */

typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
  • st_name:符号名称在字符串表中的偏移量。
  • st_value:符号的值,即符号的地址或偏移量。
    • 如果该符号在目标文件中,如果是符号的定义并且该符号不是 COMMON 块类型的则 st_value 表示该符号在段中的偏移
    • 目标文件中,如果符号是 COMMON 块类型的则 st_value 表示该符号的对齐属性
    • 可执行文件中,st_value 表示符号的虚拟地址
  • st_size:符号的大小,如果符号是一个函数,则表示函数的大小。如果该值为 0 表示符号的大小为 0 或未知。
  • st_info:该字段是一个字节,包含符号的类型和绑定信息。符号类型包括函数、数据、对象等,符号绑定包括局部符号、全局符号、弱符号等。该字段的高 4 位表示符号的类型,低 4 位表示符号的绑定信息。
  • st_other:保留字段,通常为 0 。
  • st_shndx:通常为符号所在的索引。
    • 如果符号是一个常量,该字段为 SHN_ABS(初始值不为 0 的全局变量) 或 SHN_COMMON(初始值为 0 的全局变量)。
    • 如果该符号未定义但是在该文件中被引用到,说明该符号可能定义在其他目标文件中,则该字段为 SHN_UNDEF

重定位表(.rel.text/.rel.data)

重定位表是一个 Elf*_Rel 结构的数组,每个数组元素对应一个重定位入口。重定位表主要有.rel.text.rela.text,即代码重定位节(Relocation Section)和 .rel.data.rela.data:数据重定位节(Relocation Section)。

1
2
3
4
5
6
7
/* Relocation table entry without addend (in section of type SHT_REL).  */

typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
  • r_offset
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44

    :需要进行重定位的位置的偏移量或地址。这个位置通常是指令中的某个操作数或数据的地址,需要在链接时进行修正,以便正确地引用目标符号。

    - 对于可执行文件或共享库,`r_offset` 表示需要修改的位置在内存中的位置(用于动态链接)。
    - 对于**可重定位文件**,`r_offset` 表示需要修改的位置相对于段起始位置的偏移(用于静态链接)。

    - `r_info`:低 8 位表示符号的重定位类型,重定位类型指定了进行何种类型的修正,例如绝对重定位、PC 相对重定位等。高 24 位表示该符号在**符号表**中的索引,用于解析重定位所引用的符号。

    ### 字符串表(.strtab)

    ELF 文件中用到了很多字符串,比如段名、变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。

    通过这种方法,在ELF文件中引用字符串只须给出一个数字下标即可,不用考虑字符串长度的问题。一般字符串表在ELF文件中也以段的形式保存,常见的段名为“`.strtab`”或“`.shstrtab`”。这两个字符串表分别为字符串表(String Table)和段表字符串表(Section Header String Table)。顾名思义,字符串表用来保存普通的字符串,比如符号的名字;段表字符串表用来保存段表中用到的字符串,最常见的就是段名(`sh_name` )。

    注意,在字符串表中的每个字符串的**开头**和**结尾**都有一个 `x00` 填充。

    ## 动态链接相关

    ![图片描述](https://blog-1301216421.cos.ap-chengdu.myqcloud.com/202312222350_M5UHSN8QQKV3NGG.webp)

    ### .interp 段

    在动态链接的 ELF 可执行文件中,有一个专门的段叫做 `.interp` 段(“interp”是“interpreter”(解释器)的缩写)。

    `.interp` 的内容很简单,里面保存的就是一个字符串 `/lib64/ld-linux-x86-64.so.2` ,这个字符串就是可执行文件所需要的动态链接器的路径。

    通常系统通过判断一个 ELF 程序是否有 `.interp` 来判断该 ELF 文件是否为动态链接程序。

    ### .dynamic 段

    动态链接 ELF 中最重要的结构是 `.dynamic` 段,这个段里面保存了动态链接器所需要的基本信息,比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。`.dynamic` 段是由`Elf*_Dyn` 构成的结构体数组。

    ```c
    /* Dynamic section entry. */

    typedef struct
    {
    Elf32_Sword d_tag; /* Dynamic entry type */
    union
    {
    Elf32_Word d_val; /* Integer value */
    Elf32_Addr d_ptr; /* Address value */
    } d_un;
    } Elf32_Dyn;

Elf32_Dyn 结构由一个类型值加上一个附加的数值或指针,对于不同的类型,后面附加的数值或者指针有着不同的含义。我们这里列举几个比较常见的类型值(这些值都是定义在 elf.h 里面的宏),

  • DT_SYMTAB:指定了符号表的地址,d_ptr 表示 .dynsym 的地址。
  • DT_STRTAB:指定了字符串表的地址,d_ptr 表示 .synstr 的地址。
  • DT_STRSZ:指定了字符串表的大小,d_val 表示大小。
  • DT_HASH:指定了符号哈希表的地址,用于加快符号查找的速度,d_ptr 表示 .hash 的地址。
  • DT_SONAME:指定了共享库的名称。
  • DT_RPATH:指定了库搜索路径(已废弃,不推荐使用)。
  • DT_INIT:指定了初始化函数的地址,动态链接器在加载可执行文件或共享库时会调用该函数。
  • DT_FINI:指定了终止函数的地址,动态链接器在程序结束时会调用该函数。
  • DT_NEEDED:指定了需要的共享库的名称。
  • DT_REL/DT_RELA:指定了重定位表的地址。

动态符号表(.dynsym)

为了完成动态链接,最关键的还是所依赖的符号和相关文件的信息。我们知道在静态链接中,有一个专门的段叫做符号表 .symtab(Symbol Table),里面保存了所有关于该目标文件的符号的定义和引用。为了表示动态链接这些模块之间的符号导入导出关系,ELF 专门有一个叫做动态符号表(Dynamic Symbol Table)的段用来保存这些信息,这个段的段名通常叫做 .dynsym(Dynamic Symbol),同样也是由 Elf*_Sym 构成的结构体数组。

.symtab 不同的是,.dynsym 只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有 .dynsym.symtab 两个表,.symtab 中往往保存了所有符号,包括 .dynsym 中的符号。

.symtab 类似,动态符号表也需要一些辅助的表,比如用于保存符号名的字符串表。静态链接时叫做符号字符串表 .strtab(String Table),在这里就是动态符号字符串表 .dynstr(Dynamic String Table);由于动态链接下,我们需要在程序运行时查找符号,为了加快符号的查找过程,往往还有辅助的符号哈希表(.hash)。

动态链接重定位表(.rel.dyn/.rel.data)

共享对象需要重定位的主要原因是导入符号的存在。动态链接下,无论是可执行文件或共享对象,一旦它依赖于其他共享对象,也就是说有导入的符号时,那么它的代码或数据中就会有对于导入符号的引用。在编译时这些导入符号的地址未知,在静态链接中,这些未知的地址引用在最终链接时被修正。但是在动态链接中,导入符号的地址在运行时才确定,所以需要在运行时将这些导入符号的引用修正,即需要重定位。

共享对象的重定位与我们在前面“静态链接”中分析过的目标文件的重定位十分类似,唯一有区别的是目标文件的重定位是在静态链接时完成的,而共享对象的重定位是在装载时完成的。在静态链接中,目标文件里面包含有专门用于表示重定位信息的重定位表,比如 .rel.text 表示是代码段的重定位表,.rel.data 是数据段的重定位表。

动态链接的文件中,也有类似的重定位表分别叫做 .rel.dyn.rel.plt ,它们分别相当于 .rel.data.rel.text.rel.dyn 实际上是对数据引用的修正,它所修正的位置位于 .got 以及数据段;而 .rel.plt 是对函数引用的修正,它所修正的位置位于 .got.plt

PLT 表(.plt)

在未开启 FULL RELRO 的情况下 PLT 表的结构如下图所示, PLT 表在 .plt(有的还包括 .plt.got) 中。

图片描述

PLT 表的形式如下所示:

1
2
3
4
5
6
7
8
9
10
PLT0:
push *(GOT+8)
jmp *(GOT+8)
.
.
.
bar@PLT :
jmp *(bar@PLT)
push n
jmp PLT0

其中 n 为函数 bar 在 GOT 表中的值的索引,bar@GOT 中初始值为 jmp *(bar@GOT) 指令的下一条指令,也就是说第一次调用 bar 函数的时候会继续执行跳转至 PLT0 进行 bar@GOT 的重定位并调用 bar 函数;第二次调用 bar 函数的时候由于 bar@GOT 已完成重定位因此会直接跳转至 bar 函数。

在开启 FULL RELRO 的情况下 PLT 表的结构如下图所示,此时的 PLT 表在 .plt.sec 而不是 .plt 中。

图片描述

由于 GOT 表在装载时已经完成重定位且不可写,因此不存在延迟绑定,PLT 直接根据 GOT 表存储的函数地址进行跳转。

GOT 表(.got/.got.plt)

ELF 将 GOT 拆分成了两个表叫做 .got.got.plt 。其中 .got 用来保存全局变量引用的地址,.got.plt 用来保存函数引用的地址,也就是说,所有对于外部函数的引用全部被分离出来放到了 .got.plt 中(当然有的 ELF 文件可能吧这两个表合并为一个 .got 表,结构等同于后面提到的 .got.plt)。另外 .got.plt 还有一个特殊的地方是它的前三项是有特殊意义的,分别含义如下:

  • 第一项保存的是 .dynamic 段的偏移(也有可能是 .dynamic 段的地址)。
  • 第二项是一个 link_map 的结构体指针,里面保存着动态链接的一些相关信息,是重定位函数 _dl_runtime_resolve 的第一个参数。
  • 第三项保存的是 _dl_runtime_resolve 的地址。

.got.plt 在内存中的状态如下图所示:

图片描述

静态链接程序也是有 plt 表和 got 表的,并且 plt 表也会被调用。

图片描述

辅助信息数组

无论静态还是动态链接程序都有辅助信息数组,只是动态链接程序是动态链接器使用辅助信息数组。

站在动态链接器的角度看,当操作系统把控制权交给它的时候,它将开始做链接工作,那么至少它需要知道关于可执行文件和本进程的一些信息,比如可执行文件有几个段(“Segment”)、每个段的属性、程序的入口地址(因为动态链接器到时候需要把控制权交给可执行文件)等。

这些信息往往由操作系统传递给动态链接器,保存在进程的堆栈里面。我们在前面提到过,进程初始化的时候,事实上,堆栈里面还保存了动态链接器所需要的一些辅助信息数组(Auxiliary Vector)。辅助信息的格式也是一个结构数组,它的结构被定义在 elf.h

1
2
3
4
5
6
7
8
9
10
11
typedef struct
{
uint32_t a_type; /* Entry type */
union
{
uint32_t a_val; /* Integer value */
/* We use to have pointer elements added here. We cannot do that,
though, since it does not work when using 32-bit definitions
on 64-bit platforms and vice versa. */
} a_un;
} Elf32_auxv_t;
  • a_type字段表示辅助信息数组的类型。下面是一些常见的a_type值及其对应的含义:

    • AT_NULL (0):辅助向量列表的结束标志。在列表的最后一个条目中使用。

    • AT_IGNORE (1):忽略的辅助向量类型。在某些情况下,可以将该类型的辅助向量忽略。

    • AT_EXECFD (2):可执行文件的文件描述符。表示打开可执行文件的文件描述符。

    • AT_PHDR (3):程序头表的地址。指向程序头表在内存中的起始地址。

    • AT_PHENT (4):程序头表中每个条目的大小(字节)。指示每个程序头表条目的字节数。

    • AT_PHNUM (5):程序头表的条目数量。指示程序头表中的条目数量。

    • AT_PAGESZ (6):页面大小。表示操作系统使用的页面大小。

    • AT_BASE (7):共享对象的基地址。指向主共享对象的基地址。

    • AT_FLAGS (8):标志位。包含一些特定于操作系统的标志。

    • AT_ENTRY (9):程序入口点的地址。指向程序的入口点地址。

    • AT_NOTELF (10):不是ELF文件。指示加载程序的文件不是有效的ELF文件。

  • a_un:该成员是一个联合体(union),用于存储辅助向量条目的值。在这段代码中,由于指针类型的元素会在 32 位和 64 位平台上产生兼容性问题,所以注释中提到不再添加指针元素。

  • a_val:如果辅助向量条目的类型是一个整数值,那么该成员将存储该整数值。它也是一个 32 位的无符号整数。

  • Title: ELF文件格式
  • Author: Chiu
  • Created at : 2024-07-31 13:42:16
  • Updated at : 2024-07-31 13:43:17
  • Link: https://github.com/Idealist17/github.io/2024/07/31/ELF文件格式/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments