做网站为什么选择竞网智赢,做汽车网站怎么挣钱吗,虚拟机wordpress教程,最新军事头条在计算机及嵌入式系统中#xff0c;二进制文件也有一定的标准格式#xff0c;通常会包含在各平台的应用程序二进制接口 #xff08;Application Binary Interface#xff0c;ABI#xff09;规范中。它是编译工具链必须要遵守的规范#xff08;编译工具链产生符合 ABI 的二… 在计算机及嵌入式系统中二进制文件也有一定的标准格式通常会包含在各平台的应用程序二进制接口 Application Binary InterfaceABI规范中。它是编译工具链必须要遵守的规范编译工具链产生符合 ABI 的二进制文件。
ABI 在计算机系统中应用程序二进制接口 Application Binary InterfaceABI是一个二进制程序模块之间的标准接口规范其定义了如何在机器代码中访问数据结构或计算机程序这是一种低级的、硬件相关的格式。与之对应的应用层格式被称为 APIAPI 与 ABI 是保证兼容性的重要基础。
向后二进制兼容性Backward binary compatibility即 ABI 兼容性保证使用旧版本库的程序在库升级为新版后仍可以正常运行而无需重新编译。向后源代码兼容性Backward source compatibility即 API 兼容性保证旧程序更换为新版本的库时仍然可以成功地重新编译。 ABI 规范中一个重要部分就是定义二进制文件格式标准。现代计算机系统中流行的二进制文件格式主要有 Windows 下的 PEPortable Executable 、 Linux 的 ELFExecutable and Linking Format可执行和链接格式) 以及 Apple 的 Mach-OMach object file format以下是摘自维基百科的各种二进制格式 PE 和 ELF 都是 COFFCommon Object File Format的变种而 Mach-O 最初是为 NeXT 上使用的 Mach 微内核开发的。COFF 是在 1983 年发布 Unix System V 时由 UNIX 系统实验室UNIX System LaboratoriesUSL首先提出并且使用的文件规范。 1988 年发布 System V Release 4 时UNIX 系统实验室在 COFF 的基础上开发和发布了 ELF 格式作为应用程序二进制接口 Application Binary InterfaceABI标准。 类 UNIX 系统 a.out ➔ COFF ➔ ELF 1993 年微软公司基于 COFF 格式制定了 PE 格式标准并将其用于当时的 Windows NT 3.1 系统。 DOS 系统 COM ➔ MZ ➔ NZ Windows 系统 NZ➔ PE 1995 年 Linux 开始使用 ELF 文件标准 由于 ELF 在设计之初就是跨平台的通用二进制文件格式随着 ELF 在类 Unix 系统中的普及ELF 文件很快也成为了嵌入式中应用最多的二进制文件格式。例如ARM、RISC-V 等架构都是在原始 ELF 标准结构的基础上扩展了自己的专用内容。
规范 ELF 原本是作为 System V Release 4 的一部分第四章发布的。此后不久工具接口标准委员会Tool Interface Standard CommitteeTISC就把 ELF 定义为了 Unix 系统的应用程序二进制接口 Application Binary InterfaceABI标准。现在可以在 Linux 基金会下属网站 上找到各个版本的标准文档。 TISC 共出过两个版本v1.1和 v1.2的标准文档。两个版本内容上差不多但 v1.2 版本重新组织了原本在 v1.1 版本中的内容。可读性更高。两个版本的目录如下所示 由于 TISC 的 v1.2 比较老旧且后续没有再更新尤其是在 64 位出现之后原来的 ELF v1.2 已经不再适用因此System V 对 ELF v1.2 进行了扩展成为 System V ABI Edition 4.1 版本并将其称为通用 ABIGeneric ABIgABI。 现在类 Unix 系统都使用 System V 扩展的这个 System V ABI Edition 4.1 版本作为基础。从 System V ABI Edition 4.1 开始ELF 标准实际上由两个基本部分组成
通用部分Generic ABIgABI描述了在 System V 的所有硬件实现中保持不变的接口部分。现在gABI 由 SOC 公司负责维护已经很多年不更新了最终版停留在 10 June 2013 处理器特定部分Processor Suppliment ABIpsABI描述了规范的部分特定于特定的处理器架构。psABI 则是各个架构平台厂家独立维护如下是常见架构平台的 psABI 下载地址 x86-64 psABI 目前在 Gitlab 上维护仓库地址 https://gitlab.com/x86-psABIs/x86-64-ABIi386 psABI 目前在 Gitlab 上维护仓库地址 https://gitlab.com/x86-psABIs/i386-ABIARM psABI 目前在 Github 上维护仓库地址 https://github.com/ARM-software/abi-aa。注意仓库中同时包含了 Aarch32 和 Aarch64 两架构的 psABI它俩是不兼容。RISC-V psABI 目前在 Github 上维护仓库地址 https://github.com/riscv-non-isa/riscv-elf-psabi-doc gABI 和 psABI 必须要组合使用共同构成了完整的 ELF 标准。然而有些架构平台例如ARM的 psABI 文档中已经包含了 gABI 的部分其中完整介绍了 ELF 相关内容。
UNIX Unix 是一个多任务、多用户计算机操作系统家族是第一个被广泛应用的操作系统。其设计思想被总结为 Unix 哲学是一套针对极简主义、模块化软件开发的文化规范和哲学方法对现代操作系统的设计有着重要意义 最初的 Unix 开发于 1969 年的贝尔实验室研究中心但是由于 ATT 的垄断问题美国政府禁止了 ATT 自己直接出售 Unix但是可以将 Unix 授权给了政府、学术机构以及第三方公司这就导致了出现了各种各样的 UNIX 系统现在统一称为 类 Unix 系统。 早期UNIX System V 和 Berkeley Software Distribution (BSDBSD UnixBerkeley Unix) 是 UNIX 的两个主要版本。后来ATT 被拆解1990 年代初ATT 将其 Unix 权利出售给 NovellNovell 随后将 UNIX 商标出售给 The Open Group一个成立于 1996 年的行业联盟。 现在The Open Group 负责认证符合 单一 UNIX 规范SUS 的操作系统然后授权使用 UNIX 商标等权利。MacOS 和 Linux 均已通过 SUS 认证。
编译工具链 编译工具链要编译出最终的可执行程序通常需要编译、链接、转换这三个阶段。其中编译即编译器将源码翻译成对象文件ELF 格式链接即链接器将各个对象文件组合成最终可执行程序ELF 格式。 现代编译工具链通常产生一个 ELF 格式通常是带有调试信息的最终可执行程序然后使用 ELF 处理工具从中提取出实际的纯可执行程序。目前绝大多数编译工具链都提供一系列 ELF 文件处理实用工具。
ELF 文件 ELFExecutable and Linking Format诞生于 UNIX 系统后来称为了类 UNIX 系统包括 Linux中的二进制文件的规范。用于定义不同类型的对象文件Object files中都放了什么东西、以及都以什么样的格式去放这些东西。 在 ELF 文件规范中通常把系统中采用 ELF 格式的二进制文件称为对象文件Object File并且归类为以下三种 可重定位文件Relocatable File 这类文件包含代码和数据可用来连接成可执行文件或共享对象文件Object File 静态链接库归为此类对应于 Linux 中的 .o Windows 的 .obj. 可执行文件Executable File 这类文件包含了可以直接执行的程序它的代表就是 ELF 可执行文件。 Linux 下他们一般没有扩展名比如 /bin/bashWindows 下的 .exe 共享对象文件Object FileShared Object File 这种文件包含代码和数据链接器可以使用这种文件跟其他可重定位的共享对象文件Object File链接可用于产生新的对象文件Object File或可执行文件Executable File 。 对应于 Linux 中的 .soWindows 中的 DLL动态链接器可以将几个这种共享对象文件Object File与可执行文件结合作为进程镜像文件来运行。 在 Linux 系统中还有一类文件被称为核心转储文件Core Dump File 当进程意外终止系统可以将该进程地址空间的内容及终止时的一些信息转存到核心转储文件。 对应 Linux 下的 core dump。
视图 对象文件参与程序链接构建程序和程序执行运行程序。 为了方便和高效ELF 中的对象文件Object File格式提供文件内容的不同视图反映了这些活动的不同需求。 下图显示了对象文件Object File的组织。 Program Header Table 在汇编和链接过程中没有用到所以在重定位文件中可以没有Section Header Table 中保存了所有 Section 的描述信息Section Header Table 在加载过程中没有用到对于可执行文件可以没有该部分。当然对于某些类型的文件例如shared objects来说可以同时拥有 Program header table 和 Section Header Table这样 load 完后还可以重定位。 连接视图和执行视图在编译工具链的链接脚本文件中处理的重点。关于链接脚本文件的详细介绍参见博文 Linux 之二十一 链接脚本文件 GCC 的.ld、ARMCC 的 .sct、IAR 的 .icf。 数据表示法 对象文件Object File格式支持具有 8 位字节和 32 位体系结构的各种处理器。 然而它旨在可扩展到更大或更小的体系结构。 因此对象文件Object File用一种与机器无关的格式表示一些控制数据从而可以识别对象文件Object File并以通用方式解释它们的内容。 目标处理器中的剩余数据使用目标处理器的编码而不管创建文件的机器如何。出于可移植性的原因ELF 不使用位字段。
NameSizeAlignmentPurposeElf32_Addr44Unsigned program addressElf32_Half22Unsigned medium integerElf32_Off44Unsigned file offsetElf32_Sword44Signed large integerElf32_Word44Unsigned large integerunsigned char11Unsigned small integer 对象文件格式定义的所有数据结构都遵循相关类别的自然大小和对齐准则。如果需要数据结构包含显式填充以确保 4 字节对象的 4 字节对齐强制结构大小为 4 的倍数以此类推。数据从文件开始也有适当的对齐。因此例如包含 Elf32_Addr 成员的结构将在文件中的 4 字节边界上对齐。
ELF Header ELF Header 描述了体系结构和操作系统等基本信息并指出 Section Header Table 和 Program Header Table 在文件中的什么位置。实际 ELF 文件中只有 ELF Header 位置是绝对的且只能在最开始的位置其他部分的位置顺序并不固定。 ELF Header 可以使用如下数据结构表示
#define EI_NIDENT 16
typedef struct {unsigned char e_ident[EI_NIDENT]; // MagicElf32_Half e_type; // TypeElf32_Half e_machine; // MachineElf32_Word e_version; // VersionElf32_Addr e_entry; // Entry point addressElf32_Off e_phoff; // Start of program headersElf32_Off e_shoff; // Start of section headersElf32_Word e_flags; // Flags Elf32_Half e_ehsize; // Size of this headerElf32_Half e_phentsize; // Size of program headersElf32_Half e_phnum; // Number of program headersElf32_Half e_shentsize; // Size of section headersElf32_Half e_shnum; // Number of section headersElf32_Half e_shstrndx; // Section header string table index
} Elf32_Ehdr;下面来详细介绍一下每个成员 e_ident[EI_NIDENT]使用以下宏值进行索引 名称取值意义EI_MAG00文件标识EI_MAG11文件标识EI_MAG22文件标识EI_MAG33文件标识EI_CLASS4文件类EI_DATA5数据编码EI_VERSION6文件版本EI_PAD7补齐字节开始处EI_NIDENT16e_ident[]大小e_ident[EI_MAG0] ~ e_ident[EI_MAG3]包含了 ELF 文件的魔数依次固定是 0x7f 和 ‘E’、‘L’、‘F’。 e_ident[EI_CLASS]取值如下 名称取值意义架构ELFCLASSNONE0非法类别无ELFCLASS32132 位目标AMD64 ILP32、AArch32、RV32ELFCLASS64264 位目标AMD64 LP64、AArch64、RV64e_ident[EI_DATA] 名称取值意义ELFDATANONE0非法数据编码ELFDATA2LSB1高位在前小端模式ELFDATA2MSB2低位在前大端模式e_ident[EI_VERSION]指定 ELF Header 的版本当前必须为 1。 e_ident[7]~e_ident[15]是填充符通常是 0 e_type标识对象文件类型。取值如下 名称取值意义ET_NONE0未知对象文件Object File格式ET_REL1可重定位文件ET_EXEC2可执行文件ET_DYN3共享对象文件Object FileET_CORE4Core 文件转储格式ET_LOPROC0xff00特定处理器文件 ET_LOPROC 和 ET_HIPROC 之间的取值用来标识与处理器相关的文件格式ET_HIPROC0xffff特定处理器文件 e_machine指定单个文件所需的体系结构。取值如下 NameValueMeaningEM_NONE0No machineEM_M321ATT WE 32100EM_SPARC2SPARCEM_3863Intel ArchitectureEM_68K4Motorola 68000EM_88K5Motorola 88000EM_8607Intel 80860EM_MIPS8MIPS RS3000 Big-EndianEM_MIPS_RS4_BE10MIPS RS4000 Big-EndianRESERVED11-16保留以后使用 e_version当前对象文件Object File的版本号。 名称取值意义说明EV_NONE0Invalid versionEV_CURRENT1Current version该项的取值可根据需要改变 e_entry程序的虚拟地址入口点。在 ARM 中 在可执行 ELF 文件中e_entry 是镜像唯一入口点的虚拟地址如果镜像没有唯一入口点则为 0。在可重定位ELF文件中e_entry 是被 SHF_ENTRYSECT 所标记的段的入口点的偏移量若没有入口点则为 0。Bit[0] 1表示 Thumb 指令Bit[0:1] 00表示ARM指令Bit[0:1] 10保留 平台标准可以指定可执行文件总是具有入口点在这种情况下e_entry 指定入口点即使为零。 e_phoff该成员保存了 Program Header Table 的文件偏移量以字节为单位。如果文件没有 Program Header Table则该成员值为零。 e_shoff该成员保存了 Section Header Table 的文件偏移量以字节为单位。如果文件没有 Section Header Table则该成员值为零。 e_flags是一个与处理器相关联的标志。如下是 ARM 中的定义 名称意义EF_ARM_ABIMASK (0xFF000000) (current version is 0x05000000)此 ELF 文件符合的 ARM EABI 的版本该值为一个 8 比特的掩码。 当前 EABI 是版本5。0 表示未知符合EF_ARM_BE8 (0x00800000)ELF 文件包含适合在 ARM Architecture v6 处理器上执行的 BE-8 代码。 该标志只能在可执行文件上设置EF_ARM_GCCMASK (0x00400FFF)gcc-arm-xxx 生成的旧版代码ABI 版本 4 及更早版本可能会使用这些位EF_ARM_ABI_FLOAT_HARD (0x00000400) (ABI version 5 and later)设置可执行文件头e_type ET_EXEC 或 ET_DYN以标注可执行文件的构建是为了符合硬件浮点过程调用标准。 与旧版ABI 版本 5 之前兼容gcc 用作 EF_ARM_VFP_FLOATEF_ARM_ABI_FLOAT_SOFT (0x00000200) (ABI version 5 and later)设置在可执行文件头e_type ET_EXEC 或 ET_DYN中明确标注可执行文件的构建符合软件浮点过程调用标准基准标准。 如果 EF_ARM_ABI_FLOAT_XXXX 位都清零则默认符合基本过程调用标准。 与旧版ABI 版本 5 之前兼容gcc 用作 EF_ARM_SOFT_FLOAT e_ehsize该成员保存 ELF Header 的大小以字节为单位。 e_phentsize该成员保存了 Program Header Table 中一个条目的字节大小所有条目大小相同。 e_phnum Program Header Table 中条目数量。如果文件没有 Section Header Table则该成员值为零。 e_shentsize该成员保存了 Section Header Table 中一个条目的字节大小所有条目大小相同。 e_shnumSection Header Table 中条目数量。如果文件没有 Section Header Table则该成员值为零。 e_shstrndxSection Header Table 中与节名称字符串表相关的表项的索引。如果文件没有节名称字符串表此参数可以为 SHN_UNDEF。
Section Header 节头是位于对象文件Object File中的一个表它提供了对 ELF 文件中所有节的访问。节中包含对象文件Object File中的所有信息节满足以下条件
对象文件Object File中的每个节都有对应的节头描述它反过来有节头不意味着有节。每个节占用文件中一个连续字节域这个区域可能长度为 0。文件中的节不能重叠不允许一个字节存在于两个节中的情况发生。对象文件Object File中可能包含非活动空间INACTIVE SPACE。这些区域不属于任何 头和节其内容未指定。 ELF 头中e_shoff 成员给出从文件头到节头表格的偏移字节数e_shnum 给出节头表中条目数目e_shentsize 给出每个项目的字节数。从这些信息中可以确切地定位节的具体位置、长度。节头表中比较特殊的几个下标如下
名称取值说明SHN_UNDEF0标记未定义的、缺失的、不相关的或者没有含义的节引用SHN_LORESERVEOxFF00保留索引的下界SHN_LOPROC0xFF00从此值到 SHN_HIPROC 保留给处理器特殊的语义SHN_HIPROC0XFF1F从 SHN_LOPROC 到此值保留给处理器特殊的语义SHN_ABS1包含对应引用量的绝对取值。这些值不会被重定位所 影响SHN_COMMON2相对于此节定义的符号是公共符号。如 FORTRAN 中 COMMON 或者未分配的 C 外部变量SHN_HIRESERVE保留索引的上界 注意介于 SHN_LORESERVE 和 SHN_HIRESERVE 之间的表项不会出现在节头表中。Section Header 可以用如下数据结构描述对应关系见注释
typedef struct {Elf32_Word sh_name; // nameElf32_Word sh_type; // TypeElf32_Word sh_flags; // FlgElf32_Addr sh_addr; // AddrElf32_Off sh_offset; // OffElf32_Word sh_size; // SizeElf32_Word sh_link; // LkElf32_Word sh_info; // InfElf32_Word sh_addralign; // AlElf32_Word sh_entsize; // ES
} Elf32_Shdr;sh_name给出节名称。取值是节头字符串表节Section Header String Table的索引。String Table 中的名字是一个 NULL 结尾的字符串。ELF 文件规定一些标准节的名字例如 .text、.data、.bss。 sh_type为节的内容和语义进行分类。参见下表 名称取值含义SHT_NULL0此值标志节头是非活动的没有对应的节。此节头中的其他成员取值无意义SHT_PROGBITS1此节包含程序定义的信息其格式和含义都由程序来解释释SHT_SYMTAB2此节包含一个符号表。目前对象文件Object File对每种类型的节都只能包含一个不过这个限制将来可能发生变化。一般SHT_SYMTAB 节提供用于链接编辑指 ld而言 的符号尽管也可用来实现动态链接SHT_STRTAB3此节包含字符串表。对象文件Object File可能包含多个字符串表节SHT_RELA4此节包含重定位表项其中可能会有补齐内容addend例如 32 位对象文件Object File中的 Elf32_Rela 类型。对象文件Object File可能拥有多个重定位节SHT_HASH5此节包含符号哈希表。所有参与动态链接的目标都必须包含一个符号哈希表。目前一个对象文件Object File只能包含一个哈希表 不过此限制将来可能会解除SHT_DYNAMIC6此节包含动态链接的信息。目前一个对象文件Object File中只能包含一个动态节将来可能会取消这一限制SHT_NOTE7此节包含以某种方式来标记文件的信息SHT_NOBITS8这种类型的节不占用文件中的空间其他方面和 SHT_PROGBITS 相似。尽管此节不包含任何字节成员sh_offset 中还是会包含概念性的文件偏移SHT_REL9此节包含重定位表项其中没有补齐addends例如 32 位对象文件Object File中的 Elf32_rel 类型。对象文件Object File中可以拥有多个重定位节SHT_SHLIB10此节类型是保留的但具有未指定的语义。包含这种类型的节的程序不符合 ABISHT_DYNSYM11这些节保存一个符号表SHT_LOPROC0x70000000这个范围内的值为特定于处理器的语义保留。见各架构的 psABISHT_HIPROC0x7fffffff这个范围内的值为特定于处理器的语义保留。见各架构的 psABISHT_LOUSER0x80000000该值指定了为应用程序保留的索引范围的下限SHT_HIUSER0xffffffff此值指定为应用程序保留的索引范围的上限。应用程序可以使用 SHT_LOUSER 和 SHT_HIUSER 之间的节类型而不与当前或未来系统定义的节类型冲突除了以上标准节类型外ARM 架构下还有以下特殊的类型 名称取值含义SHT_ARM_EXIDX0x70000001异常索引表SHT_ARM_PREEMPTMAP0x70000002BPABI DLL动态链接抢占地图SHT_ARM_ATTRIBUTES0x70000003对象文件兼容性属性SHT_ARM_DEBUGOVERLAY0x70000004SHT_ARM_OVERLAYSECTION0x70000005 sh_flags字段定义了一个节中包含的内容是否可以修改、是否可以执行等信息。如果一个标志比特位被设置则该位取值为 1。未定义的各位都设置为 0。 名称取值含义SHF_WRITE0x1节包含进程执行过程中将可写的数据SHF_ALLOC0x2此节在进程执行过程中占用内存。某些控制节并不出现于目标 文件的内存映像中对于那些节此位应设置为 0SHF_EXECINSTR0x4节包含可执行的机器指令SHF_MASKPROC0xF0000000所有包含于此掩码中的四位都用于处理器专用的语义ARM 中的特殊取值如下 NameValuePurposeSHF_ARM_NOREAD0x20000000本节的内容不应由程序执行者读取 sh_addr如果节将出现在进程的内存镜像中此成员给出节的第一个字节应处的位置。否则此字段为 0。 sh_link 和 sh_info根据节类型的不同sh_link 和 sh_info 的具体含义也有所不同。ARM 取值如下 sh_typesh_linksh_infoSHT_SYMTAB, SHT_DYNSYM相关联的字符串表的节头索引最后一个局部符号绑定 STB_LOCAL的符号表索引值加一SHT_DYNAMIC此节中条目所用到的字符串表格 的节头索引0SHT_HASH此哈希表所适用的符号表的节头索引0SHT_REL、SHT_RELA相关符号表的节头索引重定位所适用的节的节头索引其它SHN_UNDEF0 sh_addralign节没有最小对齐要求。 但是包含 thumb 代码的部分必须至少为 16 位对齐并且包含 ARM 代码的部分必须至少为 32 位对齐。具有 SHF_ALLOC 属性的任何节必须满足 sh_addralign 4。其他节可根据需要对齐。 例如调试表通常没有对齐要求。并且输入到静态链接器的数据段可以自然对齐。 平台标准可能会限制他们可以保证的最大对齐通常是页面大小。 sh_entsize某些节中包含固定大小的项目如符号表。对于这类节此成员给出每个表项的长度字节数。如果节中并不包含固定长度表项的表格此成员取值为 0。 sh_size此成员给出本节的长度字节数。除非节的类型是 SHT_NOBITS否则节占用文件中的 sh_size 字节。类型为 SHT_NOBITS 的节长度可能非零不过却不占用文件中的空间。 sh_offset此成员的取值给出节的第一个字节与文件头之间的偏移。不过SHT_NOBITS 类型的节不占用文件的空间因此其 sh_offset 成员给出的是其概念性的偏移。
Special Sections ELF 定义了一些具体特定意义的 Section如下表所示
节前缀名节类型节属性解释.bssSHT_NOBITSSHF_ALLOCSHF_WRITE本节保存有助于程序内存映像的未初始化数据。 根据定义当程序开始运行时系统将使用零初始化数据。 该部分不占用文件空间如段类型 SHT_NOBITS 所示.commentSHT_PROGBITSNone本节包含版本控制信息.dataSHT_PROGBITSSHF_ALLOCSHF_WRITE这些部分保存有助于程序内存映像的已初始化数据.data1SHT_PROGBITSSHF_ALLOCSHF_WRITE.debug…SHT_PROGBITSNone本节保存符号调试信息。 内容未指定。 具有前缀.debug的所有段名保留供将来使用.dynamicSHT_DYNAMICSHF_ALLOC [SHF_WRITE]本节保存动态链接信息并具有SHF_ALLOC和SHF_WRITE等属性。 操作系统和处理器确定SHF_WRITE位是否被置位.hashSHT_HASH[SHF_ALLOC]本节包含一个符号哈希表.lineSHT_PROGBITSNone本节保存符号调试的行号信息其中描述了源程序和机器代码之间的对应关系。 内容未指定.rodataSHT_PROGBITSSHF_ALLOC这些部分保存通常有助于过程映像中的不可写段的只读数据.rodata1SHT_PROGBITSSHF_ALLOC.rel name.rela nameSHT_REL SHT_RELA[SHF_ALLOC]这些节中包含了重定位信息。如果文件中 包含可加载的段段中有重定位内容节 的属性将包含 SHF_ALLOC 位否则该位 置 0。传统上 name 根据重定位所适用的节 区给定。例如 .text 节的重定位节名字将是.rel.text 或者 .rela.text.shstrtabSHT_STRTABNone本节保存节名称.strtabSHT_STRTAB[SHF_ALLOC]此节包含字符串通常是代表与符号表项 相关的名称。如果文件拥有一个可加载的 段段中包含符号串表节的属性将包含 SHF_ALLOC 位否则该位为 0.symtabSHT_SYMTAB[SHF_ALLOC]此节包含一个符号表。如果文件中包含一 个可加载的段并且该段中包含符号表那 么节的属性中包含SHF_ALLOC 位否则 该位置为 0.textSHT_PROGBITSSHF_ALLOC SHF_EXECINSTR本节包含程序的文本或可执行指令
注意 保留给处理器体系结构的节名称一般构成为处理器体系结构名称简写 节名称。且处理器名称应该与 e_machine 中使用的名称相同 对象文件Object File中也可以包含多个名字相同的节。 除了以上标准节外ARM 架构下还有以下特殊的节 节前缀名节类型节属性说明.ARM.exidx*SHT_ARM_EXIDXSHF_ALLOC SHF_LINK_ORDER以.ARM.exidx开头的节包含部分展开的索引条目.ARM.extab*SHT_PROGBITSSHF_ALLOC以.ARM.extab开头的节包含异常展开信息的名称部分.ARM.preemptmapSHT_ARM_PREEMPTMAPSHF_ALLOC以.ARM.preemptmap开头的节包含一个BPABI DLL动态链接优先地图.ARM.attributesSHT_ARM_ATTRIBUTESnone包含构建属性.ARM.debug_overlaySHT_ARM_DEBUGOVERLAYnone.ARM.overlay_tableSHT_ARM_OVERLAYSECTIONSee DBGOVL for details 这里需要注意一下 Debug Sections。Debug Sections 仅在调试时使用稍微复杂一些。ARM 可执行 ELF 文件的调试节中包含多种类型的调试信息ELF 可执行文件的使用者如armlink可以通过检查可执行文件的节表来区分这些种类型的调试信息。 ARM 系列的开发工具在不同的发展时期采用的调试信息是有区别的后来统一采用 DWARP。目前采用的应该是 3.0 版本。关于DWARF调试标准详见http://www.dwarfstd.org/。目前最新版本是 The DWARF Debugging Standard Version 5。 ASD debugging tables它们提供了与 ARM 的符号调试器的向后兼容性。ASD 调试信息存储在可执行文件中名为 .ASD 的单个 Section 中。DWARP version 1.0当链接器在 ELF 可执行文件中包含 DWARF 1.0 调试信息时该文件包含以下 ELF 节每个节都有一个节头表项: Section nameContents.debugdebugging entries.linefileinfo entries.debug_pubnamestable for accelerated access to debug items.debug_arangesaddress ranges for compilation units DWARF version 2.0当链接器在 ELF 可执行文件中包含 DWARF 2.0 调试信息时该文件包含以下 ELF 节每个节都有一个节头表项: Section nameContents.debug_infodebugging entries.debug_linefileinfo statement program.debug_pubnamestable for accelerated access to debug items.debug_arangesaddress ranges for compilation units.debug_macinfomacro information (#define / #undef).debug_framecall frame information.debugj_abbrevabbreviation table.debug_strdebug string table
Program Headers 可执行文件或者共享对象文件Object File的程序头是一个结构数组每个结构描述了一个段或者系统准备程序执行所必需的其它信息。对象文件Object File的 “段” 包含一个或者多个 “节”也就是段内容Segment Contents。程序头仅对于可执行文件和共享对象文件Object File有意义。程序头可以使用如下数据结构来表示对应关系见注释
typedef struct {Elf32_Word p_type; // TypeElf32_Off p_offset; // OffsetElf32_Addr p_vaddr; // VirtAddrElf32_Addr p_paddr; // PhyAddrElf32_Word p_filesz; // FileSizElf32_Word p_memsz; // MemSizElf32_Word p_flags; // FlgElf32_Word p_align; // Align
} Elf32_Phdr;p_type这个成员告诉这个数组元素描述什么样的段或者如何解释数组元素的信息。 类型值及其含义如下图所示。 名称取值意义PT_NULL0数组元素未使用; 其他成员的值是未定义的。 此类型使程序头表已忽略条目PT_LOAD1数组元素指定由p_filesz和p_memsz描述的可加载段PT_DYNAMIC2数组元素指定动态链接信息PT_INTERP3数组元素指定要作为解释器调用的以空值结尾的路径名的位置和大小PT_NOTE4数组元素指定辅助信息的位置和大小PT_SHLIB5该段类型是保留的但具有未指定的语义PT_PHDR6数组元素如果存在指定程序头表本身的位置和大小PT_ARM_ARCHEXT0x70000000Platform architecture compatibility informationPT_ARM_EXIDXPT_ARM_UNWIND0x70000001Exception unwind tables p_offset此成员给出从文件头到该段第一个字节的偏移p_vaddr此成员给出段的第一个字节将被放到内存中的虚拟地址。p_paddr此成员仅用于与物理地址相关的系统中。因为 System V 忽略所有应用程序的物理地址信息此字段对与可执行文件和共享对象文件Object File而言具体内容是未指定的。p_filesz此成员给出段在文件镜像中所占的字节数。可以为 0。p_memsz 此成员给出段在内存镜像中占用的字节数。可以为 0。p_flags此成员给出与段相关的标志。 名称取值意义PF_X1可执行的段PF_W2可写的段PF_R4可读的段PF_MASKPROC0xf0000000保留 p_align可加载的进程段的 p_vaddr 和 p_offset 取值必须合适相对于对页面大小的取模而言。此成员给出段在文件中和内存中如何 对齐。数值 0 和 1 表示不需要对齐。否则 p_align 应该是个正整数并且是 2 的幂次数p_vaddr 和 p_offset 对 p_align 取模后应该相等。
Symbol table 一个对象文件的符号表保存了定位和重定位所在程序的符号定义和引用所需的信息。符号表以数组的下标进行索引。0 指定表中的第一个条目并用作未定义的符号索引。符号表可以使用以下数据结构表示
typedef struct {Elf32_Word st_name; // NameElf32_Addr st_value; // ValueElf32_Word st_size; // Sizeunsigned char st_info; //unsigned char st_other; Elf32_Half st_shndx; // Ndx
} Elf32_Sym;st_name该成员将对象文件Object File的符号字符串表中的索引保存在符号名称的字符表示中 st_value该成员给出相关联的符号的值。 根据上下文这可能是绝对值地址等等; 不同对象文件Object File类型的符号表条目对 st_value 成员的解释略有不同。 在可重定位文件中st_value 保持其索引为 SHN_COMMON 的符号的对齐约束。在可重定位文件中st_value 包含已定义符号的节偏移量。 也就是说st_value 是 st_shndx 标识的部分开头的偏移量。在可执行文件和共享对象文件中st_value 包含虚拟地址 1。 为了使这些文件的符号对动态链接器更有用段偏移文件解释让位于与段号无关的虚拟地址存储器解释。 st_size许多符号具有相关尺寸。 例如数据对象的大小是对象中包含的字节数。 如果符号没有大小或未知的大小该成员将保持0。 st_info该成员指定符号的类型和绑定属性。 值和值的列表如下面两个表格所示。 以下代码显示了如何操作这些值。 #define ELF32_ST_BIND(i) ((i)4)
#define ELF32_ST_TYPE(i) ((i)0xf)
#define ELF32_ST_INFO(b,t) (((b)4)((t)0xf))符号的绑定决定了链接的可见性和行为。 NameValueMeaningSTB_LOCAL0局部符号在包含其定义的目标文件之外是不可见的。相同名称的局部符号可以存在于多个文件中而互不干扰STB_GLOBAL1全局符号对于被组合的所有目标文件都是可见的。一个文件对全局符号的定义将满足另一个文件对同一全局符号的未定义引用STB_WEAK2弱符号类似于全局符号但它们的定义优先级较低STB_LOPROC13STB_HIPROC15这个范围内的值为特定于处理器的语义保留。如果指定了含义则处理器补充解释它们。在每个符号表中所有带有 STB_LOCAL 绑定的符号都位于弱符号和全局符号之前。符号的类型提供了关联实体的一般分类。 NameValueMeaningSTT_NOTYPE0没有指定符号的类型STT_OBJECT1符号与数据对象相关联例如变量、数组等STT_FUNC2该符号与函数或其他可执行代码相关联STT_SECTION3符号与节相关联。这种类型的符号表项主要用于重定位通常具有STB_LOCAL绑定STT_FILE4一个具有 STB_LOCAL 绑定的文件符号它的 section 索引是 SHN_ABS并且它位于该文件的其他 STB_LOCAL 符号之前(如果它存在的话)STT_LOPROC13这个范围内的值为特定于处理器的语义保留。如果一个符号的值指向一个节中的特定位置那么它的节索引成员st_shndx保存着一个节头表的索引。当区段在重定位过程中移动时符号的值也会发生变化对符号的引用将继续指向程序中的相同位置。一些特殊的节索引值给出了其他语义STT_HIPROC15 st_other该成员目前只有 0没有定义。 st_shndx每个符号表条目与某些部分有关定义; 该成员保存相关部分标题表索引。 在 C 语言中符号表保存了程序实现或使用的所有全局变量和函数如果程序引用一个自身代码未定义的符号则称之为未定义符号这类引用必须在静态链接期间用其他目标模块或库解决或在加载时通过动态链接解决。
String table 字符串表节包含以 NULLASCII 码 0结尾的字符序列通常称为字符串。ELF 对象文件Object File通常使用字符串来表示符号和节名称。对字符串的引用通常以字符串在字符串表中的下标给出。 第一个字节即索引 0被定义为保存空字符。同样字符串表的最后一个字节被定义为保存空字符以确保所有字符串的空终止。索引为 0 的字符串指定无名称或空名称具体取决于上下文。允许使用空字符串表段它的 section 头的 sh_sIze 成员将包含零。对于空字符串表非零索引无效。 如上图所示字符串表索引可以引用节中的任何字节。一个字符串可以出现多次;可能存在对子字符串的引用;一个字符串可以被多次引用。也允许使用未引用的字符串。
参考
https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779https://nathanotterness.com/2021/10/tiny_elf_modernized.htmlhttps://en.wikipedia.org/wiki/Executable_and_Linkable_Formathttps://blog.k3170makan.com/2018/09/introduction-to-elf-format-elf-header.htmlhttp://blog.k3170makan.com/search?qIntroductiontotheELFFilehttps://www.ics.uci.edu/~aburtsev/238P/hw/hw3-elf/hw3-elf.html