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

news2024/11/19 13:21:15

  在计算机及嵌入式系统中,二进制文件也有一定的标准格式,通常会包含在各平台的应用程序二进制接口 (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]; // Magic
    Elf32_Half      e_type;             // Type
    Elf32_Half      e_machine;          // Machine
    Elf32_Word      e_version;          // Version
    Elf32_Addr      e_entry;            // Entry point address
    Elf32_Off       e_phoff;            // Start of program headers
    Elf32_Off       e_shoff;            // Start of section headers
    Elf32_Word      e_flags;            // Flags    
    Elf32_Half      e_ehsize;           // Size of this header
    Elf32_Half      e_phentsize;        // Size of program headers
    Elf32_Half      e_phnum;            // Number of program headers
    Elf32_Half      e_shentsize;        // Size of section headers
    Elf32_Half      e_shnum;            // Number of section headers
    Elf32_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;         // name
    Elf32_Word sh_type;         // Type
    Elf32_Word sh_flags;        // Flg
    Elf32_Addr sh_addr;         // Addr
    Elf32_Off sh_offset;        // Off
    Elf32_Word sh_size;         // Size
    Elf32_Word sh_link;         // Lk
    Elf32_Word sh_info;         // Inf
    Elf32_Word sh_addralign;    // Al
    Elf32_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;       // Type
	Elf32_Off     p_offset;     // Offset
	Elf32_Addr    p_vaddr;      // VirtAddr
	Elf32_Addr    p_paddr;      // PhyAddr
	Elf32_Word    p_filesz;     // FileSiz
	Elf32_Word    p_memsz;      // MemSiz
	Elf32_Word    p_flags;      // Flg
	Elf32_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;    // Name
	Elf32_Addr      st_value;   // Value
	Elf32_Word      st_size;    // Size
	unsigned 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.coloradmin.cn/o/1066025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数字图像处理第四版课后习题答案】第2章 数字图像基础(含英文原版)

2.1暂无 2.2 翻译答案 如图 P2.3 所示&#xff0c;视网膜图像中与点相对应的直径 x 是由类似的三角形得到的。即 得出 x 0.085d。根据第 2.1 节中的讨论&#xff0c;并结合一些自由解释&#xff0c;我们可以将眼窝视为一个方形传感器阵列&#xff0c;拥有大约 337,000 个元素…

6-3 递增的整数序列链表的插入 分数 5

List Insert(List L, ElementType X) {//创建结点List node (List)malloc(sizeof(List));node->Data X;node->Next NULL;List head L->Next; //定位real头指针//空链表 直接插入if (head NULL) {L->Next node;node->Next head;return L;}//插入数据比第…

一文全面解读CKA认证的含金量、详细介绍!

K8s是目前最流行的开源容器编排引擎&#xff0c;在全球都得到了广泛应用&#xff0c;BAT、京东、360、华为、网易、IBM、知乎等国内外诸多知名公司都在基于K8s构建企业容器云平台&#xff0c;支撑公司业务&#xff0c;越来越多的企业也都在向K8s迁移。相信在不远的将来&#xf…

winform中DevExpress控件一些属性

1.DevExpress控件bar去掉前面四点和后面的倒三角。 如图。设置bar属性optionsBar→allowQuickCustomizationFALSE

Leetcode hot 100之前缀和、差分数组、位运算

目录 差分数组-区间增减 和为K的子数组&#xff1a;前缀和 哈希表优化 除自身以外数组的乘积&#xff1a;前后缀区间 差分数组-区间增减 想对区间 nums[i..j] 的元素全部加 3&#xff0c;那么只需要让 diff[i] 3&#xff0c;然后再让 diff[j1] - 3 和为K的子数组&#x…

广州华锐互动:VR动物解剖实验室带来哪些便利?

随着科技的不断发展&#xff0c;我们的教育方式也在逐步变化和进步。其中&#xff0c;虚拟现实(VR)技术的应用为我们提供了一种全新的学习方式。尤其是在动物解剖实验中&#xff0c;VR技术不仅能够增强学习的趣味性&#xff0c;还能够提高学习效率和准确性。 由广州华锐互动开发…

Web_python_template_injection SSTI printer方法

这题挺简单的 就是记录一下不同方法的rce python_template_injection ssti了 {{.__class__.__mro__[2].__subclasses__()}} 然后用脚本跑可以知道是 71 {{.__class__.__mro__[2].__subclasses__()[71]}} 然后直接 init {{.__class__.__mro__[2].__subclasses__()[71].__i…

实战纪实 | 某米企业src未授权访问

公众号&#xff1a;掌控安全EDU 分享更多技术文章&#xff0c;欢迎关注一起探讨学习 某米企业src漏洞挖掘 这一挖就挖到了一个未授权操作漏洞&#xff0c;写个文章记录下~~ 通过信息收集&#xff0c;发现这么一个资产。 访问 http://xxx.com 如下图所示 1.点击头像-点击授权登…

【SQL Server】表死锁/解锁和sql语句分析

文章目录 表死锁查询锁的进程解锁 sql语句分析来源 表死锁 查询锁的进程 1 首先创建一个测试用的表&#xff1a; CREATE TABLE Test ( TID INT IDENTITY(1,1) ) 2 执行下面的SQL语句将此表锁住&#xff1a; SELECT * FROM Test WITH (TABLOCKX) 3 通过下面的语句可以查看…

【CMU15-445 Part-17】Two-Phase Locking

Part17-Two-Phase Locking Lock Types S-LOCK 共享锁 for reads X-LOCK 排他锁 for writes 上述T1最后R(A) 会导致不可重复读 2PL 允许数据库系统始终以保证Conflict Serializable schedule情况下分发lock Phase #1 Growing 每个txn请求locks 从lock managerlock manager…

万界星空科技云MES系统生产全流程追溯功能介绍

制造业工厂产品质量贯穿于产品的整个生命周期&#xff0c;也是企业参与市场竞争求生存求发展的基础&#xff0c;而制造过程中出现的产品质量问题则是产品最终质量的基石。 随着全球市场竞争的进一步加剧和制造业信息化进程的加快&#xff0c;企业对产品制造过程的质量信息管理…

1876. 长度为三且各字符不同的子字符串

1876. 长度为三且各字符不同的子字符串 C代码&#xff1a;滑动窗口 // 存在三种字符&#xff0c;且不重复、子串数量 int countGoodSubstrings(char * s){int k 3;int hash[26] {0};int len 0;int l 0;int ans 0;for (int i 0; i < strlen(s); i) {hash[s[i] - a];if…

c#设计模式-行为型模式 之 策略模式

&#x1f680;简介 &#x1f424;作为一个开发人员&#xff0c;开发需要选择一款开发工具&#xff0c;如在编写C#时&#xff0c;我们可以选择VisualStudio进行开发&#xff0c;也可以使用Rider 进行开发。 &#x1f433;该模式定义了一系列算法&#xff0c;并将每个算法封装起来…

最新天津python培训机构 Python培训的重要性

Python编程语言近年来备受瞩目&#xff0c;其简洁、易学、多用途的特点受到了众多程序员的喜爱。随着Python的普及程度不断提高&#xff0c;越来越多的人开始关注和学习Python。 Python培训的重要性 Python作为一门编程语言&#xff0c;具有广泛的应用领域&#xff0c;如数据…

linux5.10的一个警告Kernel image misaligned at boot, please fix your bootloader!

平台&#xff1a;rk3399linux5.10 问题&#xff1a;发现启动时有一个内核警告信息 Kernel image misaligned at boot, please fix your bootloader! 后面的数字是我打印的地址信息。 不知道这个内核搞一个这样的警告是什么原因&#xff0c;我就没有继续深挖啦。 一、通过搜…

解决若依框架多次list查询时,分页失效问题

一、问题背景 当若依框架遇到两次及以上的list查询时,会引发分页查询失效问题,如下图: 二、分析原因 分页查询原理 Mybatis的分页原理&#xff0c;大致就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL&#xff0c;实现分页…

生命不息,分享不止,5款小巧的免费软件

​ 今天继续分享5个超实用的小工具&#xff0c;都是非常小巧精致的免费软件。 1.3D渲染和动画——Luxion KeyShot ​ Luxion KeyShot是一款专业的3D渲染和动画软件&#xff0c;它可以快速地创建逼真的视觉效果。KeyShot支持多种3D格式&#xff0c;可以导入任何模型&#xff0…

【版本控制工具一】Git 安装注册及使用

文章目录 一、Git 、Github、Gitee1.1 概述1.2 码云 相对于 github 的优势 二、Github 或 Gitee注册2.1 注册2.2 创建仓库 三、Git下载与安装四、创建本地仓库 一、Git 、Github、Gitee 1.1 概述 Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或…

大数据软件项目开发流程

大数据软件项目的开发流程通常包括多个阶段&#xff0c;从项目规划和需求分析到开发、测试、部署和维护。以下是一般的大数据软件项目开发流程&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.项目规…