ELF(Executable and Linking Format)是一个二进制文件规范。用于定义不一样类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。
如今流行的二进制可执行文件格式 (Executable File Format),主要是 Windows 下的 PE(Portable Executable)和 Linux 的 ELF(Executable and Linking Format)可执行和连接格式)。他们都是 COFF(Common Object File Format)的变种。ARM 体系中采用的也是 ELF 文件格式。
COFF 是在 Unix System V Release 3 时由 UNIX 系统实验室(UNIX System Laboratories, USL)首先提出而且使用的文件规范,后来微软公司基于 COFF 格式,制定了 PE 格式标准,并将其用于当时的 Windows NT 系统。在 System V Release 4 时,UNIX 系统实验室在 COFF 的基础上,开发和发布了 ELF 格式,做为应用程序二进制接口 (Application Binary Interface,ABI)。
此后,工具接口标准委员会(Tool Interface Standard Committee,TISC)选择了正在发展中的 ELF 标准做为工做在 32 位 INTEL 体系上不一样操做系统之间可移植的二进制文件格式。能够从这里 找到详细的标准文档。以下图:
TISC 共出过两个版本(v1.1和 v1.2)的标准文档。两个版本内容上差很少,但 v1.2 版本从新组织了本来在 v1.1 版本中的内容。可读性更高。两个版本的目录以下所示:
在 ELF 文件规范中,把系统中采用 ELF 格式的文件(规范中称为对象文件(Object File))归类为如下三种:linux
在 Linux 系统中,还有一类文件,被称为核心转储文件(Core Dump File) ,当进程意外终止,系统能够将该进程地址空间的内容及终止时的一些信息转存到核心转储文件。 对应 Linux 下的 core dump。
对象文件参与程序连接(构建程序)和程序执行(运行程序)。 为了方便和高效,对象文件(Object File)格式提供文件内容的并行视图,反映了这些活动的不一样需求。 下图显示了对象文件(Object File)的组织。
其中,各部分的含义都是规范定义好的!
web
对象文件(Object File)格式支持具备 8 位字节和 32 位体系结构的各类处理器。 然而,它旨在可扩展到更大(或更小)的体系结构。 所以,对象文件(Object File)用一种与机器无关的格式表示一些控制数据,从而能够识别对象文件(Object File)并以通用方式解释它们的内容。 目标处理器中的剩余数据使用目标处理器的编码,而无论建立文件的机器如何。出于可移植性的缘由,ELF 不使用位字段。编程
Name | Size | Alignment | Purpose |
---|---|---|---|
Elf32_Addr | 4 | 4 | Unsigned program address |
Elf32_Half | 2 | 2 | Unsigned medium integer |
Elf32_Off | 4 | 4 | Unsigned file offset |
Elf32_Sword | 4 | 4 | Signed large integer |
Elf32_Word | 4 | 4 | Unsigned large integer |
unsigned char | 1 | 1 | Unsigned small integer |
对象文件格式定义的全部数据结构都遵循相关类的天然大小和对齐准则。若是须要,数据结构包含显式填充,以确保4字节对象的4字节对齐,强制结构大小为4的倍数,以此类推。数据从文件开始也有适当的对齐。所以,例如,包含Elf32 Addr成员的结构将在文件中的4字节边界上对齐。数组
ELF 中对于符号的字符编码也有必定的要求。当 ELF 接口文档提到字符常量时,例如’/‘或’\ n’,它们的数值应遵循 7 位 ASCII 准则。 对于先前的字符常量,单字节值分别为 47 和 10。
根据字符编码,在 0 到 127 范围以外的字符值能够占用一个或多个字节。 应用程序能够根据须要使用不一样语言的不一样字符集扩展来控制本身的字符集。 尽管 TIS-一致性 不限制字符集,但它们一般应遵循一些简单的指导原则:bash
关于 ELF 文件规范这里就很少作详细介绍了,感兴趣的能够去 Linux 基金会的官方网站下载规范来看看!数据结构
ARM 体系中,全部文件均采用的 ELF 文件格式。咱们能够在 ARM 的官网找到 ARM 关于 ARM ELF 文件格式的说明文档。后文参考部分的下载中是目前能够从 ARM 官网找到的全部和 ARM ELF 相关的 PDF 文档。
目前,咱们能够找到的 ARM ELF 相关的文档主要有 4 个:《ARM ELF File Format》、《ELF for the ARM® Architecture》、《ARM ELF》以及 ARM 的连接器手册。其中,《ARM ELF File Format》是比较早期的文档,针对于 ARM SDT 时代的 ELF 文件,有点过期了;后者三个则是最新的介绍文档,《ELF for the ARM® Architecture》 仅仅是对 ARM ELF 取值的一些特殊说明,是在读者先了解 ELF 文件规范的基础上进行的说明。
ARM 中的各类源文件(包括汇编文件,C 语言程序及 C++ 程序等)通过 ARM 编译器编译后生成 ELF 格式的对象文件(Object File)(.o文件)。这些对象文件(Object File)和相应的 C/C++ 运行时用到的库通过 ARM 链接器处理后,生成 ELF 格式的镜像文件(image),这种ELF 格式的映像文件是一种可执行文件,可被写入嵌入式设备的 ROM 中。
在 ARM 体系中,全部的二进制文件均被称为对象文件。其中,连接器最终生成的 ELF 格式的可执行文件又被称为镜像文件(Image file)。ARM ELF 镜像文件或者对象文件由输入节(Input Sections)、输出节(Output Sections)、域(Regions) 和 段(Segments) 组成,每一个连接阶段都有不一样的镜像视图。以下图所示:
架构
ELF object file view (linker input): ELF 对象文件视图由输入节组成。 ELF 对象文件能够是:ide
Linker view: 链接器视图针对程序地址空间会有两个视图。而且这两个视图在存在重叠,位置无关和可重定位的程序片断(代码或数据)时变得不一样:svg
若是片断与位置无关或可重定位,则其执行地址在执行期间可能会有所不一样。函数
ELF image file view (linker output): ELF 镜像文件视图由程序段和输出节组成:
一个或多个执行域组成一个加载域。
When describing a memory view:
- The term root region means a region that has the same load and execution addresses.
- Load regions are equivalent to ELF segments.
一个输入节就是是输入对象文件中的一个独立的部分。 它包含代码,初始化数据,或着是描述未初始化或必须在镜像文件执行前设置为零的内存片断。 这些属性由 RO,RW,XO 和 ZI 等属性表示。 armlink
使用这些属性将输入节分组为更大的构建块,称为输出节和域。
一个输出节就是一组输入节的组合,它们具备相同的 RO,RW,XO 或 ZI 属性,而且由连接器连续放置在存储器中。 输出节与组成它的输入节具备相同的属性。 在输出节中,输入节根据节放置规则进行排序。
一个域最多包含四个输出节,具体取决于内容和具备不一样属性的节的数量。 默认状况下,域中的输出节根据其属性进行排序。 首先是 XO 属性的输出节,而后是 RO 属性的输出节,再而后是 RW 属性的输出节,最后是 ZI 属性的输出节。 域一般会映射到物理存储设备,例如 ROM,RAM 或外围设备。 您可使用分散加载文件来更改输出节的顺序。
一个程序段对应于一个加载域,而且包含执行域。 程序段包含文本和数据等信息。
存在 XO( execute-only)节时的注意事项
- 您能够在同一执行域中混合 XO 和 非 XO 节。 可是,输出的结果是一个 RO 节。
- 若是输入文件具备一个或多个 XO 节,则连接器将生成单独的 XO ELF 段。 在最终镜像中,除非使用分散加载文件或
--xo-base
选项另有指定,不然 XO 段紧接在 RO 段以前。
镜像的域在加载时放置在系统存储器映射中。 内存中域的位置可能会在执行期间发生变化。在执行镜像以前,可能必须将镜像的某些域移动到其执行地址并建立 ZI 输出节。 例如,初始化的 RW 数据可能必须从其 ROM 中的加载地址复制到 RAM 中的执行地址。镜像的内存映射具备如下不一样视图:
根据镜像加载到内存中时所处的地址,即镜像执行开始前的位置,描述每一个镜像的域和节。
根据镜像执行期间所在的地址描述每一个镜像的域和节。
下图显示了没有仅执行(XO)节的镜像的这些视图:
下图显示了具备 XO 节的镜像的加载和执行视图:
镜像中的入口点就是镜像中的一个位置(地址),该位置(地址)会被加载到 PC 寄存器。 它是程序执行开始的位置。 虽然镜像中能够有多个入口点,但在连接时只能指定一个入口点。并不是每一个 ELF 文件都必须有入口点。 不容许在单个 ELF 文件中存在多个入口点。
对于嵌入式 Cortex-M 核的程序,程序的执行是从复位向量所在的位置(地址)开始执行。复位向量会被加载到 PC 寄存器中,且复位向量的位置(地址)并不固定。 一般,复位向量指向 CMSIS Reset_Handler 函数。
有两种不一样类型的入口点:
__main
就是入口点。若是加载程序要使用嵌入式的映像,则它必须在标头中指定一个初始入口点。 使用--entry
命令行选项选择入口点。
与标准的 ELF 文件相比,ARM ELF 的某些值比较特殊,下面以实际文件来讲明一下每一个部分。编译工具以下图:
编译后,会在对应目录下生成 .o 文件和 .axf 文件,为了分析 ELF 文件,咱们将使用 readelf 工具。在详细解析以前,先用 Winhex 直接打开生成的 .o 文件,能够看到文件开头有 ELF 字样。代表它是一个 ELF 文件。以下:
注意:.o 不是 ARM 的可执行文件!axf 为可执行文件。如下用两种程序做对比。
一个简单的可执行 ARM ELF 文件的概念布局以下图所示。请注意,文件中各部分的实际排序可能与下图中的顺序不一样,由于只有 ELF Header 在文件中具备固定位置。
注意,针对目前最新版本的 ARM ELF,上图有点过期!
ELF Header 描述了体系结构和操做系统等基本信息,并指出 Section Header Table 和 Program Header Table 在文件中的什么位置。实际文件中,只有 ELF Header 位置是绝对的,且只能在最开始,其余部分部分的位置顺序并不必定彻底相同。
Program Header Table 在汇编和连接过程当中没有用到,因此在重定位文件中能够没有;Section Header Table 中保存了全部 Section 的描述信息,Section Header Table 在加载过程当中没有用到,对于可执行文件,能够没有该部分。固然,对于某些类型的文件来讲,能够同时拥有 Program header table 和 Section Header Table,这样 load 完后还能够重定位。(例如:shared objects)
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;
下面两幅图分别显示了不一样文件的 ELF Header。以上数据结构中的注释,即对应于下图中的各部分字段。
.o 文件 ELF Header 以下图所示:
.axf 文件 ELF Header 以下图所示:
下面对以上两幅图中的内容作一下详细介绍:
unsigned char e_ident[EI_NIDENT];
,使用如下宏值进行索引:
名称 | 取值 | 意义 |
---|---|---|
EI_MAG0 | 0 | 文件标识 |
EI_MAG1 | 1 | 文件标识 |
EI_MAG2 | 2 | 文件标识 |
EI_MAG3 | 3 | 文件标识 |
EI_CLASS | 4 | 文件类 |
EI_DATA | 5 | 数据编码 |
EI_VERSION | 6 | 文件版本 |
EI_PAD | 7 | 补齐字节开始处 |
EI_NIDENT | 16 | e_ident[]大小 |
e_ident[EI_MAG0] ~ e_ident[EI_MAG3]
:包含了ELF文件的魔数,依次是 0x7f 和 ‘E’、‘L’、‘F’ 的 ASCII。e_ident[EI_CLASS]
:取值以下
名称 | 取值 | 意义 |
---|---|---|
ELFCLASSNONE | 0 | 非法类别 |
ELFCLASS32 | 1 | 32 |
ELFCLASS64 | 2 | 64 |
e_ident[EI_DATA]
:
名称 | 取值 | 意义 |
---|---|---|
ELFDATANONE | 0 | 非法数据编码 |
ELFDATA2LSB | 1 | 高位在前 |
ELFDATA2MSB | 2 | 低位在前 |
e_ident[EI_VERSION]
:指定 ELF头部的版本,当前必须为1。e_ident[7]~e_ident[15]
:是填充符,一般是0e_ident[EI_CLASS]
。取值 | 意义 |
---|---|
ELFOSABI_ARM_AEABI (64) | 该对象包含符号版本控制扩展,如§3.1.1符号版本控制中所述。 |
名称 | 取值 | 意义 |
---|---|---|
ET_NONE | 0 | 未知对象文件(Object File)格式 |
ET_REL | 1 | 可重定位文件 |
ET_EXEC | 2 | 可执行文件 |
ET_DYN | 3 | 共享对象文件(Object File) |
ET_CORE | 4 | Core 文件(转储格式) |
ET_LOPROC | 0xff00 | 特定处理器文件 ET_LOPROC 和 ET_HIPROC 之间的取值用来标识与处理器相关的文件格式。 |
ET_HIPROC | 0xffff | 特定处理器文件 |
Name | Value | Meaning |
---|---|---|
EM_NONE | 0 | No machine |
EM_M32 | 1 | AT&T WE 32100 |
EM_SPARC | 2 | SPARC |
EM_386 | 3 | Intel Architecture |
EM_68K | 4 | Motorola 68000 |
EM_88K | 5 | Motorola 88000 |
EM_860 | 7 | Intel 80860 |
EM_MIPS | 8 | MIPS RS3000 Big-Endian |
EM_MIPS_RS4_BE | 10 | MIPS RS4000 Big-Endian |
…… | ||
EM_ARM | 40 | ARM/Thumb Architecture |
名称 | 取值 | 意义 | 说明 |
---|---|---|---|
EV_NONE | 0 | Invalid version | |
EV_CURRENT | 1 | Current version | 该项的取值可根据须要改变 |
名称 | 意义 |
---|---|
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)以标注可执行文件的构建是为了符合硬件浮点过程调用标准。 与旧版(预版本5)兼容,gcc用做EF_ARM_VFP_FLOAT |
EF_ARM_ABI_FLOAT_SOFT (0x00000200) (ABI version 5 and later) | 设置在可执行文件头(e_type = ET_EXEC或ET_DYN)中明确标注可执行文件的构建符合软件浮点过程调用标准(基准标准)。 若是EF_ARM_ABI_FLOAT_XXXX位都清零,则默认符合基本过程调用标准。 与旧版(预版本5)兼容,gcc用做EF_ARM_SOFT_FLOAT。 |
注意:实际文件中,每一部分的位置顺序并不必定彻底相同,只有ELF Header位置是绝对的,且只能在最开始。
节头表提供了对 ELF 文件中全部节的访问。节中包含对象文件(Object File)中的全部信息,除了:ELF 头部、程序头部表格、节头部 表格。节知足如下条件:
ELF 头部中,e_shoff 成员给出从文件头到节头部表格的偏移字节数;e_shnum给出表格中条目数目;e_shentsize 给出每一个项目的字节数。从这些信息中能够确切地定位节的具体位置、长度。节头部表格中比较特殊的几个下标以下:
名称 | 取值 | 说明 |
---|---|---|
SHN_UNDEF | 0 | 标记未定义的、缺失的、不相关的,或者没有含义的节引用 |
SHN_LORESERVE | OxFF00 | 保留索引的下界 |
SHN_LOPROC | 0xFF00 | SHN_HIPROC 0XFF1F 保留给处理器特殊的语义 |
SHN_ABS | 1 | 包含对应引用量的绝对取值。这些值不会被重定位所 影响 |
SHN_COMMON | 2 | 相对于此节定义的符号是公共符号。如 FORTRAN 中 COMMON 或者未分配的 C 外部变量。 |
SHN_HIRESERVE | 保留索引的上界 |
介于 SHN_LORESERVE 和 SHN_HIRESERVE 之间的表项不会出如今节头部表中。
.o文件 Section Header(部分)
.axf 文件 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 Section)的索引。名字是一个 NULL 结尾的字符串。ELF 文件规定一些标准节的名字,例如.text、.data、.bss。此外,如上图中,许多节名字都是ARM本身扩展的。
sh_type:为节的内容和语义进行分类。ARM ELF 只使用了其中的一部分。参见下表(部分)。
名称 | 取值 | 含义 |
---|---|---|
SHT_NULL | 0 | 此值标志节头部是非活动的,没有对应的节。此节头部中的其余成员取值无心义。 |
SHT_PROGBITS | 1 | 此节包含程序定义的信息,其格式和含义都由程序来解释释。 |
SHT_SYMTAB | 2 | 此节包含一个符号表。目前对象文件(Object File)对每种类型的节都只能包含一个,不过这个限制未来可能发生变化。通常,SHT_SYMTAB 节提供用于连接编辑(指 ld而言) 的符号,尽管也可用来实现动态连接。 |
SHT_STRTAB | 3 | 此节包含字符串表。对象文件(Object File)可能包含多个字符串表节。 |
SHT_RELA | 4 | 此节包含重定位表项,其中可能会有补齐内容(addend),例如 32 位对象文件(Object File)中的 Elf32_Rela 类型。对象文件(Object File)可能拥有多个重定位节。 |
SHT_HASH | 5 | 此节包含符号哈希表。全部参与动态连接的目标都必须包含一个符号哈希表。目前,一个对象文件(Object File)只能包含一个哈希表, 不过此限制未来可能会解除。 |
SHT_DYNAMIC | 6 | 此节包含动态连接的信息。目前一个对象文件(Object File)中只能包含一个动态节,未来可能会取消这一限制。 |
SHT_NOTE | 7 | 此节包含以某种方式来标记文件的信息。 |
SHT_NOBITS | 8 | 这种类型的节不占用文件中的空间,其余方面和SHT_PROGBITS 类似。尽管此节不包含任何字节,成员sh_offset 中仍是会包含概念性的文件偏移 |
SHT_REL | 9 | 此节包含重定位表项,其中没有补齐(addends),例如 32 位对象文件(Object File)中的 Elf32_rel 类型。对象文件(Object File)中能够拥有多个重定位节。 |
除了以上标准节类型外,ARM 架构下,还有如下特殊的类型:
名称 | 取值 | 含义 |
---|---|---|
SHT_ARM_EXIDX | 0x70000001 | 异常索引表 |
SHT_ARM_PREEMPTMAP | 0x70000002 | BPABI DLL动态连接抢占地图 |
SHT_ARM_ATTRIBUTES | 0x70000003 | 对象文件兼容性属性 |
SHT_ARM_DEBUGOVERLAY | 0x70000004 | |
SHT_ARM_OVERLAYSECTION | 0x70000005 |
sh_flags:字段定义了一个节中包含的内容是否能够修改、是否能够执行等信息。若是一个标志比特位被设置,则该位取值为1。未定义的各位都设置为 0。
名称 | 取值 | 含义 |
---|---|---|
SHF_WRITE | 0x1 | 节包含进程执行过程当中将可写的数据 |
SHF_ALLOC | 0x2 | 此节在进程执行过程当中占用内存。某些控制节并不出现于目标 文件的内存映像中,对于那些节,此位应设置为 0 |
SHF_EXECINSTR | 0x4 | 节包含可执行的机器指令 |
SHF_MASKPROC | 0xF0000000 | 全部包含于此掩码中的四位都用于处理器专用的语义 |
ARM 中的特殊取值以下:
Name | Value | Purpose |
---|---|---|
SHF_ARM_NOREAD | 0x20000000 | 本节的内容不该由程序执行者读取 |
sh_addr:若是节将出如今进程的内存镜像中,此成员给出节的第一个字节应处的位置。不然,此字段为 0。
sh_link 和 sh_info:根据节类型的不一样,sh_link 和 sh_info 的具体含义也有所不一样。ARM 取值以下:
sh_type | sh_link | sh_info |
---|---|---|
SHT_SYMTAB, SHT_DYNSYM | 相关联的字符串表的节头部索引 | 最后一个局部符号(绑定 STB_LOCAL)的符号表索引值加一 |
SHT_DYNAMIC | 此节中条目所用到的字符串表格 的节头部索引 | 0 |
SHT_HASH | 此哈希表所适用的符号表的节头部索引 | 0 |
SHT_REL、SHT_RELA | 相关符号表的节头部索引 | 重定位所适用的节的节头部索引 |
其它 | SHN_UNDEF | 0 |
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 成员给出的是其概念性的偏移。
注意:
ARM 节名称是如下面列出的具备预约义含义的标准前缀之一开始的名称,或者是包含美圆($)字符的名称。 在 ARM EABI 下没有其余具备特殊意义的段名称。
节前缀名 | 节类型 | 节属性 | 解释 |
---|---|---|---|
.bss | SHT_NOBITS | SHF_ALLOC+SHF_WRITE | 本节保存有助于程序内存映像的未初始化数据。 根据定义,当程序开始运行时,系统将使用零初始化数据。 该部分不占用文件空间,如段类型 SHT_NOBITS 所示。 |
.comment | SHT_PROGBITS | None | 本节包含版本控制信息 |
.data | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE | 这些部分保存有助于程序内存映像的已初始化数据 |
.data1 | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE | |
.debug… | SHT_PROGBITS | None | 本节保存符号调试信息。 内容未指定。 具备前缀.debug的全部段名保留供未来使用 |
.dynamic | SHT_DYNAMIC | SHF_ALLOC [+SHF_WRITE] | 本节保存动态连接信息,并具备SHF_ALLOC和SHF_WRITE等属性。 操做系统和处理器肯定SHF_WRITE位是否被置位 |
.hash | SHT_HASH | [SHF_ALLOC] | 本节包含一个符号哈希表。 |
.line | SHT_PROGBITS | None | 本节保存符号调试的行号信息,其中描述了源程序和机器代码之间的对应关系。 内容未指定 |
.rodata | SHT_PROGBITS | SHF_ALLOC | 这些部分保存一般有助于过程映像中的不可写段的只读数据 |
.rodata1 | SHT_PROGBITS | SHF_ALLOC | |
.rel name .rela name |
SHT_REL SHT_RELA | [SHF_ALLOC] | 这些节中包含了重定位信息。若是文件中 包含可加载的段,段中有重定位内容,节 的属性将包含 SHF_ALLOC 位,不然该位 置 0。传统上 name 根据重定位所适用的节 区给定。例如 .text 节的重定位节名字,将是:.rel.text 或者 .rela.text。 |
.shstrtab | SHT_STRTAB | None | 本节保存节名称。 |
.strtab | SHT_STRTAB | [SHF_ALLOC] | 此节包含字符串,一般是表明与符号表项 相关的名称。若是文件拥有一个可加载的 段,段中包含符号串表,节的属性将包含 SHF_ALLOC 位,不然该位为 0。 |
.symtab | SHT_SYMTAB | [SHF_ALLOC] | 此节包含一个符号表。若是文件中包含一 个可加载的段,而且该段中包含符号表,那 么节的属性中包含SHF_ALLOC 位,不然 该位置为 0。 |
.text | SHT_PROGBITS | SHF_ALLOC+ SHF_EXECINSTR | 本节包含程序的文本或可执行指令 |
除了以上标准节外,ARM 架构下,还有如下特殊的节:
节前缀名 | 节类型 | 节属性 | 说明 |
---|---|---|---|
.ARM.exidx* | SHT_ARM_EXIDX | SHF_ALLOC + SHF_LINK_ORDER | 以.ARM.exidx开头的节包含部分展开的索引条目。 |
.ARM.extab* | SHT_PROGBITS | SHF_ALLOC | 以.ARM.extab开头的节包含异常展开信息的名称部分。 |
.ARM.preemptmap | SHT_ARM_PREEMPTMAP | SHF_ALLOC | 以.ARM.preemptmap开头的节包含一个BPABI DLL动态连接优先地图。 |
.ARM.attributes | SHT_ARM_ATTRIBUTES | none | 包含构建属性 |
.ARM.debug_overlay | SHT_ARM_DEBUGOVERLAY | none | |
.ARM.overlay_table | SHT_ARM_OVERLAYSECTION | See DBGOVL for details |
这里须要注意一下 Debug Sections。Debug Sections 仅在调试时使用,稍微复杂一些。ARM 可执行 ELF 文件的调试节中包含多种类型的调试信息,ELF 可执行文件的使用者(如armlink)能够经过检查可执行文件的节表来区分这些种类型的调试信息。
ARM 系列的开发工具在不一样的发展时期,采用的调试信息是有区别的,后来统一采用 DWARP。目前采用的应该是 3.0 版本。具体以下:
Section name | Contents |
---|---|
.debug | debugging entries |
.line | fileinfo entries |
.debug_pubnames | table for accelerated access to debug items |
.debug_aranges | address ranges for compilation units |
Section name | Contents |
---|---|
.debug_info | debugging entries |
.debug_line | fileinfo statement program |
.debug_pubnames | table for accelerated access to debug items |
.debug_aranges | address ranges for compilation units |
.debug_macinfo | macro information (#define / #undef) |
.debug_frame | call frame information |
.debugj_abbrev | abbreviation table |
.debug_str | debug string table |
关于DWARF调试标准详见:http://www.dwarfstd.org/。目前最新版本是The DWARF Debugging Standard Version 5
可执行文件或者共享对象文件(Object File)的程序头部是一个结构数组,每一个结构描述了一个段或者系统准备程序执行所必需的其它信息。对象文件(Object File)的"段"包含一个或者多个"节",也就是"段内容(Segment Contents)"。程序头部仅对于可执行文件和共享对象文件(Object File)有意义。
图7 Program Header
程序头可使用以下数据结构来表示(对应关系见注释):
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;
名称 | 取值 | 意义 |
---|---|---|
PT_NULL | 0 | 数组元素未使用; 其余成员的值是未定义的。 此类型使程序头表已忽略条目。 |
PT_LOAD | 1 | 数组元素指定由p_filesz和p_memsz描述的可加载段。 |
PT_DYNAMIC | 2 | 数组元素指定动态连接信息。 |
PT_INTERP | 3 | 数组元素指定要做为解释器调用的以空值结尾的路径名的位置和大小。 |
PT_NOTE | 4 | 数组元素指定辅助信息的位置和大小。 |
PT_SHLIB | 5 | 该段类型是保留的,但具备未指定的语义。 |
PT_PHDR | 6 | 数组元素(若是存在)指定程序头表自己的位置和大小。 |
PT_ARM_ARCHEXT | 0x70000000 | Platform architecture compatibility information |
PT_ARM_EXIDX PT_ARM_UNWIND |
0x70000001 | Exception unwind tables |
名称 | 取值 | 意义 |
---|---|---|
PF_X | 1 | 可执行的段 |
PF_W | 2 | 可写的段 |
PF_R | 4 | 可读的段 |
PF_MASKPROC | 0xf0000000 | 保留 |
一个对象文件的符号表保存了定位和重定位所在程序的符号定义和引用所需的信息。符号表以数组的下标进行索引。0 指定表中的第一个条目,并用做未定义的符号索引。ARM 结构中,符号表与标准的 ELF 文件没有任何区别。
图12 .o文件 Symbol table(部分)
在 C 语言中,符号表保存了程序实现或使用的全部全局变量和函数,若是程序引用一个自身代码未定义的符号,则称之为未定义符号,这类引用必须在静态连接期间用其余目标模块或库解决,或在加载时经过动态连接解决。
符号表可使用如下数据结构表示:
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;
#define ELF32_ST_BIND(i) ((i)>>4) #define ELF32_ST_TYPE(i) ((i)&0xf) #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))A symbol’s binding determines the linkage visibility and behavior.
Name | Value | Meaning |
---|---|---|
STT_NOTYPE | 0 | The symbol’s type is not specified. |
STT_OBJECT | 1 | The symbol is associated with a data object, such as a variable, an array, and so on. |
STT_FUNC | 2 | The symbol is associated with a function or other executable code. |
STT_SECTION | 3 | The symbol is associated with a section. Symbol table entries of this type exist primarily for relocation and normally have STB_LOCAL binding. |
STT_FILE | 4 | A file symbol has STB_LOCAL binding, its section index is SHN_A BS, and it precedes the other STB_LOCAL symbols for the file, if it is present. |
STT_LOPROC | 13 | Values in this inclusive range are reserved for processor-specific semantics. If a symbol’s value refers to a specific location within a section, its section index member, st_shndx, holds an index into the section header table. As the section moves during relocation, the symbol’s value changes as well, and references to the symbol continue to point to the same location in the program. Some special section index values give other semantics. |
STT_HIPROC | 15 |
Name | Value | Meaning |
---|---|---|
STT_NOTYPE | 0 | The symbol’s type is not specified. |
STT_OBJECT | 1 | The symbol is associated with a data object, such as a variable, an array, and so on. |
STT_FUNC | 2 | The symbol is associated with a function or other executable code. |
STT_SECTION | 3 | The symbol is associated with a section. Symbol table entries of this type exist primarily for relocation and normally have STB_LOCAL binding. |
STT_FILE | 4 | A file symbol has STB_LOCAL binding, its section index is SHN_A BS, and it precedes the other STB_LOCAL symbols for the file, if it is present. |
STT_LOPROC | 13 | Values in this inclusive range are reserved for processor-specific semantics. If a symbol’s value refers to a specific location within a section, its section index member, st_shndx, holds an index into the section header table. As the section moves during relocation, the symbol’s value changes as well, and references to the symbol continue to point to the same location in the program. Some special section index values give other semantics. |
STT_HIPROC | 15 |
The symbols in ELF object files convey specific information to the linker and loader. See section 4, ARM- and Thumb-Specific Definitions, for a description of the actual linking model used in the system.
As mentioned above, the symbol table entry for index 0 (STN_UNDEF) is reserved. It is shown in Figure 3-18. Figure 3-18, Symbol Table Entry: Index 0
Name | Value | Note |
---|---|---|
st_name | 0 | No name |
st_value | 0 | Zero value |
st_size | 0 | No size |
st_info | 0 | No type, local binding |
st_other | 0 | |
st_shndx | SHN_UNDEF | No section |
字符串表节包含以 NULL(ASCII 码 0)结尾的字符序列,一般称为字符串。ELF 对象文件(Object File)一般使用字符串来表示符号和节名称。对字符串的引用一般以字符串在字符 串表中的下标给出。ARM结构中,字符串表与标准的 ELF 文件没有任何区别。
axf 文件是 ARM 的调试文件,其格式符合上一节讲的对象文件(Object File)格式(ELF)。其中除了包含了完整的 bin 文件外,还附加了其余的调试信息。在调试的时候,这些调试信息是没必要下到 RAM 中去的,真正下到 RAM 中的信息仅仅是可执行代码。下图为 axf 文件的头部。
经过直接查看完整的 axf 文件能够看出,axf 中绝大多数都是和调试相关的内容。真正的 Bin 只是其中的一小部分。Bin 的结尾处在 axf 文件中也很容易找到,再次就不在赘述。
既然前面咱们说了,axf 文件就是 ELF 文件格式,那么咱们可使用 readelf 工具,具体查看一下axf文件。下图是一个 axf 文件的节表
bin 文件是 ARM 的可执行文件,是最纯粹的二进制机器代码。与HEX文件包括地址信息的不一样,BIN文件格式只包括了数据自己。在烧写或下载HEX文件的时候,通常都不须要用户指定地址,由于HEX文件内部的信息已经包括了地址。而烧写BIN文件的时候,用户是必定须要指定地址信息的。
ARM 的 Bin 文件就是 axf 的精华部分(掐掉ELF头,去掉 .symtab、.debug和.symtab区里的信息)。下图是笔者使用Winhex截取的ARM的Bin文件的开头和结尾的示意图。
首先,hex 文件最初由 Intel 提出。在 Intel HEX 文件中,每一行是一个 HEX 记录,由十六进制数组成的机器码或者数据常量,Intel HEX 文件常常被用于将程序或数据传输存储到 ROM、EPROM,大多数编程器和模拟器使用 Intel HEX 文件。
hex 文件所有由可打印的 ASCII 字符组成。以下图就是 ARM-MDK5.22 生成的一个hex文件(部分)
从上图不难看出,hex 文件就是一个个的十六进制的字符串。实际上,一个 Intel HEX 文件能够包含任意多的十六进制记录,每条记录有五个域,每条记录都由一个冒号":"打头。一个数据记录以一个回车和一个换行结束。其格式以下:
:CCAAAARR[DD...]ZZ
其中:
举例以下:
:10400000781A00203D420008034C0008D14B0008FC
下载 相关文档的 PDF 文档