编译工具链 之二 详解 ELF 格式及标准、UNIX 发展、ABI

  在计算机及嵌入式系统中,二进制文件也有一定的标准格式,通常会包含在各平台的应用程序二进制接口 (Application Binary Interface,ABI)规范中。它是编译工具链必须要遵守的规范(编译工具链产生符合 ABI 的二进制文件)。

ABI

  在计算机系统中,应用程序二进制接口 (Application Binary Interface,ABI)是一个二进制程序模块之间的标准接口规范,其定义了如何在机器代码中访问数据结构或计算机程序,这是一种低级的、硬件相关的格式。与之对应的应用层格式被称为 API,API 与 ABI 是保证兼容性的重要基础。

  • 向后二进制兼容性(Backward binary compatibility,即 ABI 兼容性)保证使用旧版本库的程序在库升级为新版后仍可以正常运行,而无需重新编译。
  • 向后源代码兼容性(Backward source compatibility,即 API 兼容性)保证旧程序更换为新版本的库时仍然可以成功地重新编译。

  ABI 规范中一个重要部分就是定义二进制文件格式标准。现代计算机系统中流行的二进制文件格式主要有 Windows 下的 PE(Portable Executable)Linux 的 ELF(Executable and Linking Format)可执行和链接格式) 以及 Apple 的 Mach-O(Mach object file format),以下是摘自维基百科的各种二进制格式:
在这里插入图片描述
  PE 和 ELF 都是 COFF(Common Object File Format)的变种,而 Mach-O 最初是为 NeXT 上使用的 Mach 微内核开发的。COFF 是在 1983 年发布 Unix System V 时由 UNIX 系统实验室(UNIX System Laboratories,USL)首先提出并且使用的文件规范。

  1. 1988 年发布 System V Release 4 时,UNIX 系统实验室在 COFF 的基础上,开发和发布了 ELF 格式,作为应用程序二进制接口 (Application Binary Interface,ABI)标准。

    类 UNIX 系统: a.out ➔ COFF ➔ ELF

  2. 1993 年微软公司基于 COFF 格式,制定了 PE 格式标准,并将其用于当时的 Windows NT 3.1 系统。

    DOS 系统: COM ➔ MZ ➔ NZ; Windows 系统: NZ➔ PE

  3. 1995 年 Linux 开始使用 ELF 文件标准

  由于 ELF 在设计之初就是跨平台的通用二进制文件格式,随着 ELF 在类 Unix 系统中的普及,ELF 文件很快也成为了嵌入式中应用最多的二进制文件格式。例如,ARM、RISC-V 等架构都是在原始 ELF 标准结构的基础上扩展了自己的专用内容。

规范

  ELF 原本是作为 System V Release 4 的一部分(第四章)发布的。此后不久,工具接口标准委员会(Tool Interface Standard Committee,TISC)就把 ELF 定义为了 Unix 系统的应用程序二进制接口 (Application Binary Interface,ABI)标准。现在可以在 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 版本,并将其称为通用 ABI(Generic ABI,gABI)。

  现在,类 Unix 系统都使用 System V 扩展的这个 System V ABI Edition 4.1 版本作为基础。从 System V ABI Edition 4.1 开始,ELF 标准实际上由两个基本部分组成:

  • 通用部分(Generic ABI,gABI):描述了在 System V 的所有硬件实现中保持不变的接口部分。现在,gABI 由 SOC 公司负责维护,已经很多年不更新了,最终版停留在 10 June 2013
    在这里插入图片描述
  • 处理器特定部分(Processor Suppliment ABI,psABI):描述了规范的部分特定于特定的处理器架构。psABI 则是各个架构平台厂家独立维护,如下是常见架构平台的 psABI 下载地址:
    • x86-64 psABI: 目前在 Gitlab 上维护,仓库地址 https://gitlab.com/x86-psABIs/x86-64-ABI
    • i386 psABI: 目前在 Gitlab 上维护,仓库地址 https://gitlab.com/x86-psABIs/i386-ABI
    • ARM psABI: 目前在 Github 上维护,仓库地址 https://github.com/ARM-software/abi-aa。注意,仓库中同时包含了 Aarch32Aarch64 两架构的 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 年的贝尔实验室研究中心,但是由于 AT&T 的垄断问题,美国政府禁止了 AT&T 自己直接出售 Unix,但是,可以将 Unix 授权给了政府、学术机构以及第三方公司,这就导致了出现了各种各样的 UNIX 系统(现在统一称为 类 Unix 系统)。
在这里插入图片描述
  早期,UNIX System V 和 Berkeley Software Distribution (BSD,BSD Unix,Berkeley Unix) 是 UNIX 的两个主要版本。后来,AT&T 被拆解,1990 年代初,AT&T 将其 Unix 权利出售给 Novell,Novell 随后将 UNIX 商标出售给 The Open Group(一个成立于 1996 年的行业联盟)。
在这里插入图片描述
  现在,The Open Group 负责认证符合 单一 UNIX 规范(SUS) 的操作系统,然后授权使用 UNIX 商标等权利。MacOS 和 Linux 均已通过 SUS 认证。

编译工具链

  编译工具链要编译出最终的可执行程序,通常需要编译、链接、转换这三个阶段。其中,编译即编译器将源码翻译成对象文件(ELF 格式),链接即链接器将各个对象文件组合成最终可执行程序(ELF 格式)。
在这里插入图片描述
  现代编译工具链通常产生一个 ELF 格式(通常是带有调试信息)的最终可执行程序,然后使用 ELF 处理工具从中提取出实际的纯可执行程序。目前,绝大多数编译工具链都提供一系列 ELF 文件处理实用工具。
在这里插入图片描述

ELF 文件

  ELF(Executable 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/bash;Windows 下的 .exe

  • 共享对象文件(Object File)(Shared Object File ): 这种文件包含代码和数据,链接器可以使用这种文件跟其他可重定位的共享对象文件(Object File)链接,可用于产生新的对象文件(Object File)或可执行文件(Executable File )。

    1. 对应于 Linux 中的 .so,Windows 中的 DLL
    2. 动态链接器可以将几个这种共享对象文件(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 tableSection Header Table,这样 load 完后还可以重定位。

连接视图和执行视图在编译工具链的链接脚本文件中处理的重点。关于链接脚本文件的详细介绍参见博文 Linux 之二十一 链接脚本文件 GCC 的.ld、ARMCC 的 .sct、IAR 的 .icf。

数据表示法

  对象文件(Object File)格式支持具有 8 位字节和 32 位体系结构的各种处理器。 然而,它旨在可扩展到更大(或更小)的体系结构。 因此,对象文件(Object File)用一种与机器无关的格式表示一些控制数据,从而可以识别对象文件(Object File)并以通用方式解释它们的内容。 目标处理器中的剩余数据使用目标处理器的编码,而不管创建文件的机器如何。出于可移植性的原因,ELF 不使用位字段。

NameSizeAlignmentPurpose
Elf32_Addr44Unsigned program address
Elf32_Half22Unsigned medium integer
Elf32_Off44Unsigned file offset
Elf32_Sword44Signed large integer
Elf32_Word44Unsigned large integer
unsigned char11Unsigned small integer

  对象文件格式定义的所有数据结构都遵循相关类别的自然大小和对齐准则。如果需要,数据结构包含显式填充,以确保 4 字节对象的 4 字节对齐,强制结构大小为 4 的倍数,以此类推。数据从文件开始也有适当的对齐。因此,例如,包含 Elf32_Addr 成员的结构将在文件中的 4 字节边界上对齐。

ELF Header

  ELF Header 描述了体系结构和操作系统等基本信息,并指出 Section Header TableProgram 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、RV32
    ELFCLASS64264 位目标AMD64 LP64、AArch64、RV64

    e_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 File)
    ET_CORE4Core 文件(转储格式)
    ET_LOPROC0xff00特定处理器文件 ET_LOPROC 和 ET_HIPROC 之间的取值用来标识与处理器相关的文件格式
    ET_HIPROC0xffff特定处理器文件
  • e_machine:指定单个文件所需的体系结构。取值如下:

    NameValueMeaning
    EM_NONE0No machine
    EM_M321AT&T WE 32100
    EM_SPARC2SPARC
    EM_3863Intel Architecture
    EM_68K4Motorola 68000
    EM_88K5Motorola 88000
    EM_8607Intel 80860
    EM_MIPS8MIPS RS3000 Big-Endian
    EM_MIPS_RS4_BE10MIPS RS4000 Big-Endian
    RESERVED11-16保留以后使用
  • e_version:当前对象文件(Object File)的版本号。

    名称取值意义说明
    EV_NONE0Invalid version
    EV_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_EXECET_DYN)以标注可执行文件的构建是为了符合硬件浮点过程调用标准。 与旧版(ABI 版本 5 之前)兼容,gcc 用作 EF_ARM_VFP_FLOAT
    EF_ARM_ABI_FLOAT_SOFT (0x00000200) (ABI version 5 and later)设置在可执行文件头(e_type = ET_EXECET_DYN)中明确标注可执行文件的构建符合软件浮点过程调用标准(基准标准)。 如果 EF_ARM_ABI_FLOAT_XXXX 位都清零,则默认符合基本过程调用标准。 与旧版(ABI 版本 5 之前)兼容,gcc 用作 EF_ARM_SOFT_FLOAT
  • e_ehsize:该成员保存 ELF Header 的大小(以字节为单位)。

  • e_phentsize:该成员保存了 Program Header Table 中一个条目的字节大小,所有条目大小相同。

  • e_phnumProgram 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)中的所有信息,节满足以下条件:

  1. 对象文件(Object File)中的每个节都有对应的节头描述它,反过来,有节头不意味着有节。
  2. 每个节占用文件中一个连续字节域(这个区域可能长度为 0)。
  3. 文件中的节不能重叠,不允许一个字节存在于两个节中的情况发生。
  4. 对象文件(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_LORESERVESHN_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此节类型是保留的,但具有未指定的语义。包含这种类型的节的程序不符合 ABI
    SHT_DYNSYM11这些节保存一个符号表
    SHT_LOPROC0x70000000这个范围内的值为特定于处理器的语义保留。见各架构的 psABI
    SHT_HIPROC0x7fffffff这个范围内的值为特定于处理器的语义保留。见各架构的 psABI
    SHT_LOUSER0x80000000该值指定了为应用程序保留的索引范围的下限
    SHT_HIUSER0xffffffff此值指定为应用程序保留的索引范围的上限。应用程序可以使用 SHT_LOUSER 和 SHT_HIUSER 之间的节类型,而不与当前或未来系统定义的节类型冲突

    除了以上标准节类型外,ARM 架构下,还有以下特殊的类型:

    名称取值含义
    SHT_ARM_EXIDX0x70000001异常索引表
    SHT_ARM_PREEMPTMAP0x70000002BPABI DLL动态链接抢占地图
    SHT_ARM_ATTRIBUTES0x70000003对象文件兼容性属性
    SHT_ARM_DEBUGOVERLAY0x70000004
    SHT_ARM_OVERLAYSECTION0x70000005
  • sh_flags:字段定义了一个节中包含的内容是否可以修改、是否可以执行等信息。如果一个标志比特位被设置,则该位取值为 1。未定义的各位都设置为 0。

    名称取值含义
    SHF_WRITE0x1节包含进程执行过程中将可写的数据
    SHF_ALLOC0x2此节在进程执行过程中占用内存。某些控制节并不出现于目标 文件的内存映像中,对于那些节,此位应设置为 0
    SHF_EXECINSTR0x4节包含可执行的机器指令
    SHF_MASKPROC0xF0000000所有包含于此掩码中的四位都用于处理器专用的语义

    ARM 中的特殊取值如下:

    NameValuePurpose
    SHF_ARM_NOREAD0x20000000本节的内容不应由程序执行者读取
  • sh_addr:如果节将出现在进程的内存镜像中,此成员给出节的第一个字节应处的位置。否则,此字段为 0。

  • sh_linksh_info:根据节类型的不同,sh_linksh_info 的具体含义也有所不同。ARM 取值如下:

    sh_typesh_linksh_info
    SHT_SYMTAB, SHT_DYNSYM相关联的字符串表的节头索引最后一个局部符号(绑定 STB_LOCAL)的符号表索引值加一
    SHT_DYNAMIC此节中条目所用到的字符串表格 的节头索引0
    SHT_HASH此哈希表所适用的符号表的节头索引0
    SHT_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_ALLOC+SHF_WRITE本节保存有助于程序内存映像的未初始化数据。 根据定义,当程序开始运行时,系统将使用零初始化数据。 该部分不占用文件空间,如段类型 SHT_NOBITS 所示
.commentSHT_PROGBITSNone本节包含版本控制信息
.dataSHT_PROGBITSSHF_ALLOC+SHF_WRITE这些部分保存有助于程序内存映像的已初始化数据
.data1SHT_PROGBITSSHF_ALLOC+SHF_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 name
SHT_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本节包含程序的文本或可执行指令

注意:

  1. 保留给处理器体系结构的节名称一般构成为:处理器体系结构名称简写 + 节名称。且处理器名称应该与 e_machine 中使用的名称相同

  2. 对象文件(Object File)中也可以包含多个名字相同的节。

  3. 除了以上标准节外,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 information
    PT_ARM_EXIDX
    PT_ARM_UNWIND
    0x70000001Exception 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_vaddrp_offset 取值必须合适,相对于对页面大小的取模而言。此成员给出段在文件中和内存中如何 对齐。数值 0 和 1 表示不需要对齐。否则 p_align 应该是个正整数,并且是 2 的幂次数,p_vaddrp_offsetp_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_valuest_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))
    

    符号的绑定决定了链接的可见性和行为。

    NameValueMeaning
    STB_LOCAL0局部符号在包含其定义的目标文件之外是不可见的。相同名称的局部符号可以存在于多个文件中而互不干扰
    STB_GLOBAL1全局符号对于被组合的所有目标文件都是可见的。一个文件对全局符号的定义将满足另一个文件对同一全局符号的未定义引用
    STB_WEAK2弱符号类似于全局符号,但它们的定义优先级较低
    STB_LOPROC13
    STB_HIPROC15这个范围内的值为特定于处理器的语义保留。如果指定了含义,则处理器补充解释它们。

    在每个符号表中,所有带有 STB_LOCAL 绑定的符号都位于弱符号和全局符号之前。符号的类型提供了关联实体的一般分类。

    NameValueMeaning
    STT_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

  字符串表节包含以 NULL(ASCII 码 0)结尾的字符序列,通常称为字符串。ELF 对象文件(Object File)通常使用字符串来表示符号和节名称。对字符串的引用通常以字符串在字符串表中的下标给出。

  第一个字节,即索引 0,被定义为保存空字符。同样,字符串表的最后一个字节被定义为保存空字符,以确保所有字符串的空终止。索引为 0 的字符串指定无名称或空名称,具体取决于上下文。允许使用空字符串表段,它的 section 头的 sh_sIze 成员将包含零。对于空字符串表,非零索引无效。
在这里插入图片描述
  如上图所示,字符串表索引可以引用节中的任何字节。一个字符串可以出现多次;可能存在对子字符串的引用;一个字符串可以被多次引用。也允许使用未引用的字符串。

参考

  1. https://gist.github.com/x0nu11byt3/bcb35c3de461e5fb66173071a2379779
  2. https://nathanotterness.com/2021/10/tiny_elf_modernized.html
  3. https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
  4. https://blog.k3170makan.com/2018/09/introduction-to-elf-format-elf-header.html
  5. http://blog.k3170makan.com/search?q=Introduction+to+the+ELF+File
  6. https://www.ics.uci.edu/~aburtsev/238P/hw/hw3-elf/hw3-elf.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/151416.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt单一应用实例判断

原本项目中使用QSharedMemory的方法来判断当前是否已存在运行的实例&#xff0c;但在MacOS上&#xff0c;当程序异常崩溃后&#xff0c;QSharedMemory没有被正常销毁&#xff0c;导致应用程序无法再次被打开。 对此&#xff0c;Qt assistant中有相关说明&#xff1a; 摘抄 qt-s…

tailscale自建headscale和derp中继

tailscale自建headscale和derp中继 Tailscale 官方的 DERP 中继服务器全部在境外&#xff0c;在国内的网络环境中不一定能稳定连接&#xff0c;所以有必要建立自己的 DERP 服务器的。 准备工作&#xff1a; 需要有自己的云服务器&#xff0c;本示例为阿里云轻量服务器需要有…

Spring的beanName生成器AnnotationBeanNameGenerator

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

11.3 读图举例

一、低频功率放大电路 图11.3.1所示为实用低频功率放大电路&#xff0c;最大输出功率为 7 W 7\,\textrm W 7W。其中 A \textrm A A 的型号为 LF356N&#xff0c; T 1 T_1 T1​ 和 T 3 T_3 T3​ 的型号为 2SC1815&#xff0c; T 4 T_4 T4​ 的型号为 2SD525&#xff0c; T 2…

(高阶) Redis 7 第21讲 IO多路复用模型 完结篇

🌹 以下分享 Redis IO多路复用模型,如有问题请指教。🌹🌹 如你对技术也感兴趣,欢迎交流。🌹🌹🌹 如有对阁下帮助,请👍点赞💖收藏🐱‍🏍分享😀 IO多路复用模型是什么 I/O:网络IO 多路:多个客户端连接(连接即套接字描述符,即socket或channel),指…

leetcode 49. 字母异位词分组

2023.10.7 根据字母异位词的定义&#xff0c;可知&#xff1a;所有字母异位词经过排序之后得到的字符串相同&#xff0c;所以可以定义一个哈希表&#xff0c;将排序后的字符串当作哈希表的键&#xff0c;哈希表的值则用来存储该字母异位词对应的所有字符串&#xff0c;最后将哈…

HDLbits: Shift18

先补充一下算术移位寄存器和按位移位寄存器&#xff1a; SystemVerilog具有按位和算术移位运算符。 按位移位只是将向量的位向右或向左移动指定的次数&#xff0c;移出向量的位丢失。移入的新位是零填充的。例如&#xff0c;操作8’b11000101 << 2将产生值8’b00010100…

【数据结构-二叉树 八】【遍历求和】:求根到叶子节点数字之和

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【遍历求和】&#xff0c;使用【二叉树】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&am…

练[SUCTF 2019]CheckIn

[SUCTF 2019]CheckIn 文章目录 [SUCTF 2019]CheckIn掌握知识解题思路关键paylaod 掌握知识 ​ .user.ini文件上传利用–需要上传目录有一个php文件(index.php)&#xff0c;文件头绕过&#xff0c;文件内容<&#xff1f;检测 解题思路 打开题目链接&#xff0c;发现又是一…

[SWPUCTF 2021 新生赛]easy_sql - 联合注入||报错注入||sqlmap

[SWPUCTF 2021 新生赛]easy_sql 一、思路分析二、解题方法解法一&#xff1a;手注解法二&#xff1a;报错注入解法三&#xff1a;sqlmap 一、思路分析 这题可以直接参考&#xff1a;[NISACTF 2022]join-us - 报错注入&无列名注入 网站标题提示&#xff0c;参数是wllm ?…

day25--JS进阶(递归函数,深浅拷贝,异常处理,改变this指向,防抖及节流)

目录 浅拷贝 1.拷贝对象①Object.assgin() ②展开运算符newObj {...obj}拷贝对象 2.拷贝数组 ①Array.prototype.concat() ② newArr [...arr] 深拷贝 1.通过递归实现深拷贝 2.lodash/cloneDeep实现 3.通过JSON.stringify()实现 异常处理 throw抛异常 try/catch捕获…

Linux TCP协议通信 (流程 三次握手 四次挥手 滑动窗口)

TCP通信流程 Socket函数 TCP通信实现&#xff08;服务器端&#xff09; #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() {//1.创建socketint lfd socket(AF_INET, SOCK_…

kafka的请求处理机制

目录 前言&#xff1a; kafak是如何处理请求的&#xff1f; 控制请求与数据类请求 参考资料 前言&#xff1a; 无论是 Kafka 客户端还是 Broker 端&#xff0c;它们之间的交互都是通过“请求 / 响应”的方式完成的。比如&#xff0c;客户端会通过网络发送消息生产请求给 B…

四位十进制频率计VHDL,DE1开发板验证,仿真和源码

名称&#xff1a;四位十进制频率计VHDL&#xff0c;DE1开发板验证 软件&#xff1a;Quartus 语言&#xff1a;VHDL 要求&#xff1a; 数字频率计设计要求 1、四位十进制数字显示的数学式频率计,其频率测量范围为10~9999khz,测量单位为kHz。 2、要求量程能够转换。即测几十…

蓝桥杯每日一题2023.10.8

题目描述 七段码 - 蓝桥云课 (lanqiao.cn) 题目分析 所有的情况我们可以分析出来一共有2的7次方-1种&#xff0c;因为每一个二极管都有选择和不选择两种情况&#xff0c;有7个二极管&#xff0c;但是还有一种都不选的情况需要排除&#xff0c;故-1 枚举每个方案看是否符合要…

【图像处理】【应用程序设计】加载,编辑和保存图像数据、图像分割、色度键控研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MongoDB数据库网站网页实例-编程语言Python+Django

程序示例精选 PythonDjangoMongoDB数据库网站网页实例 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjangoMongoDB数据库网站网页实例》编写代码&#xff0c;代码整洁&#xff0c;…

【ONE·Linux || 多线程(二)】

总言 多线程&#xff1a;生产者消费者模型与两种实现方式&#xff08;条件变量、信号量&#xff09;、线程池。 文章目录 总言4、生产者消费者模型4.1、基本概念4.2、基于BlockingQueue的生产者消费者模型&#xff08;理解条件变量&#xff09;4.2.1、单生产者单消费者模式&am…

golang gin——中间件编程以及jwt认证和跨域配置中间件案例

中间件编程jwt认证 在不改变原有方法的基础上&#xff0c;添加自己的业务逻辑。相当于grpc中的拦截器一样&#xff0c;在不改变grpc请求的同时&#xff0c;插入自己的业务。 简单例子 func Sum(a, b int) int {return a b }func LoggerMiddleware(in func(a, b int) int) f…

一文搞懂UART通信协议

目录 1、UART简介 2、UART特性 3、UART协议帧 3.1、起始位 3.2、数据位 3.3、奇偶校验位 3.4、停止位 4、UART通信步骤 1、UART简介 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器&#xff09;是一种双向、串行、异步的通信…