可执行文件或共享库文件的程序头表是一个结构数组。每种结构都描述了系统准备程序执行所需的段或其他信息。目标文件段包含一个或多个节,如段内容中所述。
程序头仅对可执行文件和共享库文件有意义。文件使用 ELF 头的 e_phentsize 和 e_phnum 成员来指定各自的程序头大小。
程序头具有以下结构。 请参见 sys/elf.h。
typedef struct { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } Elf32_Phdr; typedef struct { Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf64_Xword p_align; } Elf64_Phdr;
此数组元素描述的段类型或解释此数组元素的信息的方式。表 7–25 中指定了类型值及其含义。
相对段的第一个字节所在文件的起始位置的偏移。
段在与物理寻址相关的系统中的物理地址。由于此系统忽略了应用程序的物理地址,因此该成员对于可执行文件和共享库具有未指定的内容。
段的文件映像中的字节数,可以为零。
段的内存映像中的字节数,可以为零。
与段相关的标志。表 7–26 中指定了类型值及其含义。
可装入的进程段必须具有 p_vaddr 和 p_offset 的同余值(以页面大小为模数)。此成员可提供一个值,用于在内存和文件中根据该值对齐各段。值 0 和 1 表示无需对齐。另外,p_align 应为 2 的正整数幂,并且 p_vaddr 应等于 p_offset(以 p_align 为模数)。 请参见程序装入(特定于处理器)。
某些项用于描述进程段。其他项则提供补充信息,并且不会构成进程映像。除非明确指定了顺序,否则段的各项可以任何顺序显示。下表中列出了定义的类型值。
表 7–25 ELF 段类型
名称 |
值 |
---|---|
PT_NULL |
0 |
PT_LOAD |
1 |
PT_DYNAMIC |
2 |
PT_INTERP |
3 |
PT_NOTE |
4 |
PT_SHLIB |
5 |
PT_PHDR |
6 |
PT_TLS |
7 |
PT_LOOS |
0x60000000 |
PT_SUNW_UNWIND |
0x6464e550 |
PT_LOSUNW |
0x6ffffffa |
PT_SUNWBSS |
0x6ffffffa |
PT_SUNWSTACK |
0x6ffffffb |
PT_SUNWDTRACE |
0x6ffffffc |
PT_SUNWCAP |
0x6ffffffd |
PT_HISUNW |
0x6fffffff |
PT_HIOS |
0x6fffffff |
PT_LOPROC |
0x70000000 |
PT_HIPROC |
0x7fffffff |
未使用。没有定义成员值。使用此类型,程序头表可以包含忽略的项。
指定可装入段,通过 p_filesz 和 p_memsz 进行描述。文件中的字节会映射到内存段的起始位置。如果段的内存大小 (p_memsz) 大于文件大小 (p_filesz),则将多余字节的值定义为 0。这些字节跟在段的已初始化区域后面。文件大小不能大于内存大小。程序头表中的可装入段的各项按升序显示,并基于 p_vaddr 成员进行排列。
指定动态链接信息。 请参见动态节。
指定要作为解释程序调用的以空字符结尾的路径名的位置和大小。对于动态可执行文件,必须设置此类型。此类型可出现在共享库中。此类型不能在一个文件中多次出现。此类型(如果存在)必须位于任何可装入段的各项的前面。 有关详细信息,请参见程序的解释程序。
指定辅助信息的位置和大小。 有关详细信息,请参见注释节。
保留类型,但具有未指定的语义。
指定程序头表在文件及程序内存映像中的位置和大小。此段类型不能在一个文件中多次出现。此外,仅当程序头表是程序内存映像的一部分时,才可以出现此段。此类型(如果存在)必须位于任何可装入段的各项的前面。 有关详细信息,请参见程序的解释程序。
指定线程局部存储模板。 有关详细信息,请参见线程局部存储节。
此段包含栈扩展表。
与 PT_LOAD 元素相同的属性,用于描述 .SUNW_bss 节。
描述进程栈。只能存在一个 PT_SUNWSTACK 元素。仅访问权限(如 p_flags 字段中所定义)有意义。
保留供 dtrace(1M) 内部使用。
指定硬件功能要求。 有关详细信息,请参见硬件和软件功能节。
除非在其他位置具体要求,否则所有程序头的段类型都是可选的。文件的程序头表只能包含与其内容相关的那些元素。
可执行文件和共享库文件都有一个基本地址,该地址是与程序目标文件的内存映像关联的最低虚拟地址。基本地址的其中一种用途是在动态链接过程中重定位程序的内存映像。
可执行文件或共享库文件的基本地址是在执行过程中通过以下三个值计算得出的:内存装入地址、最大页面大小和程序可装入段的最低虚拟地址。程序头中的虚拟地址可能并不表示程序内存映像的实际虚拟地址。 请参见程序装入(特定于处理器)。
要计算基本地址,首先需要确定与 PT_LOAD 段的最低 p_vaddr 值关联的内存地址。然后,将内存地址截断为最大页面大小的最接近倍数,从而获取基本地址。根据装入内存的文件的类型,内存地址可能与 p_vaddr 值不匹配。
系统要装入的程序必须至少包含一个可装入段,即使文件格式并不要求此限制也是如此。系统创建可装入段的内存映像时,将会授予如 p_flags 成员中所指定的访问权限。PF_MASKPROC 掩码中包括的所有位都保留用于特定于处理器的语义。
表 7–26 ELF 段标志
名称 |
值 |
含义 |
---|---|---|
PF_X |
0x1 |
执行 |
PF_W |
0x2 |
写 |
PF_R |
0x4 |
读 |
PF_MASKPROC |
0xf0000000 |
未指定 |
如果权限位是 0,则会拒绝该位的访问类型。实际内存权限取决于内存管理单元,该单元可随系统的不同而变化。尽管所有标志组合均有效,但系统仍可授予比请求更多的访问权限。不过,如果不显式指定写权限,则段在任何情况下都不会具有该权限。下表列出了确切的标志解释及允许的标志解释。
表 7–27 ELF 段权限
标志 |
值 |
确切解释 |
允许解释 |
---|---|---|---|
无 |
0 |
拒绝所有访问 |
拒绝所有访问 |
PF_X |
1 |
仅执行 |
读、执行 |
PF_W |
2 |
只写 |
读、写、执行 |
PF_W + PF_X |
3 |
写、执行 |
读、写、执行 |
PF_R |
4 |
只读 |
读、执行 |
PF_R + PF_X |
5 |
读、执行 |
读、执行 |
PF_R + PF_W |
6 |
读、写 |
读、写、执行 |
PF_R + PF_W + PF_X |
7 |
读、写、执行 |
读、写、执行 |
例如,典型的文本段具有读和执行权限,但没有写权限。数据段通常具有读、写和执行权限。
目标文件段由一节或多节组成,但此事实对程序头是透明的。另外,无论文件段包含一节还是包含多节,对程序装入都没有实际意义。但是,必须存在各种数据以便执行程序、进行动态链接等操作。下图使用一般术语说明了段内容。段中各节的顺序和成员关系可能会有所变化。
文本段包含只读指令和数据。数据段包含可写数据和指令。有关所有特殊节的列表,请参见表 7–10。
PT_DYNAMIC 程序头元素指向 .dynamic 节。.got 和 .plt 节还包含与位置无关的代码和动态链接的相关信息。
.plt 可以位于文本或数据段中,具体取决于处理器。 有关详细消息,请参见全局偏移表(特定于处理器)和过程链接表(特定于处理器)。
类型为 SHT_NOBITS 的节不会占用文件空间,但却可构成段的内存映像。通常,这些未初始化的数据驻留在段尾,从而使 p_memsz 大于关联程序头元素中的 p_filesz。