ELF 零散信息

ELF 文件规范

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)。

TISC ELF 文档

ELF 文档

TISC 共出过两个版本(v1.1和 v1.2)的标准文档。两个版本内容上差不多,但 v1.2 版本重新组织了原本在 v1.1 版本中的内容。可读性更高。两个版本的目录如下所示:

ELF 对象文件

在 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)。对应于Linux 中的 .so,Windows 中的 DLL另外是动态链接器可以将几个这种共享对象文件(Object File)与可执行文件结合,作为进程镜像文件来运行。

 在 Linux 系统中,还有一类文件,被称为核心转储文件(Core Dump File) ,当进程意外终止,系统可以将该进程地址空间的内容及终止时的一些信息转存到核心转储文件。 对应 Linux 下的 core dump。

对象文件参与程序链接(构建程序)和程序执行(运行程序)。 为了方便和高效,对象文件(Object File)格式提供文件内容的并行视图,反映了这些活动的不同需求。 下图显示了对象文件(Object File)的组织。

ARM ELF 文件格式

   目前,我们可以找到的 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) 组成,每个链接阶段都有不同的镜像视图。如下图所示:

可执行文件的构成

  • 一个可执行文件通常由不同的段构成: 代码段、数据段、BSS段、只读数据段等。
  • 每个section 都用一个section header 来描述,包括段名、段的类型、段的起始地址、段的偏移和段的大小等。
  • 将setion headers 集中放到一起,就是section header table, 翻译成中文就是节头表,
  • 知道了可执行文件的基本构成,就是将C程序中定义的函数、变量、挑挑拣拣加以分类,分别放置在可执行文件的代码段、数据段和BSS 段。 
SectionNotescheck
ELF Header 描述文件类型、要运行的处理器平台、入口地址等,当程序运行时,加载器会根据此文件头来获取可执行文件的一些信息。   
program header table    
.init C语言库的一些汇编代码,用来初始化C程序运行所依赖的环境,如内存堆栈的初始化等。  
.text 代码段  
.rodata  程序中定义的一些字符串、printf函数打印的字符串常量   
.data 数据段  
.bss 未初始化的全局变量和静态变量,他们没有初始化,默认值全部是0.BSS 段不占用空间  BSS 段的大小,起始地址和各个变量的地址信息会section header table 和符号表里面 
.debug  debug 模式下,用来保存可执行文件中每一条二进制指令对应的源码位置信息。根据这些信息,GDB 调试器就可以支持源码级的单步调试。  
.line    
.strtab    
section header table 每个section 的section header `readelf -s`查看节头表

bss 段
1. 未初始化的全局变量和静态变量,他们没有初始化,默认值全部是0.BSS 段不占用空间
2. BSS 段的大小,起始地址和各个变量的地址信息会section header table 和符号表里面
3. 当程序运行时,加载器会根据这些信息在内存中紧挨着数据段后面为BSS段开辟一片空间,为各个变量分配存储空间。 

Image entry points

镜像中的入口点就是镜像中的一个位置(地址),该位置(地址)会被加载到 PC 寄存器。 它是程序执行开始的位置。 虽然镜像中可以有多个入口点,但在链接时只能指定一个入口点。并非每个 ELF 文件都必须有入口点。 不允许在单个 ELF 文件中存在多个入口点。

  对于嵌入式 Cortex-M 核的程序,程序的执行是从复位向量所在的位置(地址)开始执行。复位向量会被加载到 PC 寄存器中,且复位向量的位置(地址)并不固定。 通常,复位向量指向 CMSIS Reset_Handler 函数。
有两种不同类型的入口点:

  • 初始化入口点:镜像的初始入口点是存储在 ELF 头文件中的单个值。 对于那些需要由操作系统或引导加载程序加载到 RAM 中的程序,加载程序通过将控制转移到镜像中的初始入口点来启动镜像执行。一个镜像只能有一个初始化入口点。初始入口点可以是 ENTRY 指令设置的入口点之一,但不是必需的。
  • ENTRY 指令指定的入口点:可以为镜像从多个可能的入口点中选择其中一个。每个镜像只能有一个入口点。您可以在汇编程序文件中使用 ENTRY 指令在对象中创建入口点。 在嵌入式系统中,该指令的典型用途是标记进入处理器异常向量(例如 RESET,IRQ 和 FIQ)的代码。该指令使用 ENTRY 关键字标记输出代码部分,该关键字指示链接器在执行未使用的部分消除时不删除该部分。对于 C/C++ 程序,C 库 中的 __main 就是入口点。

如果加载程序要使用嵌入式的映像,则它必须在标头中指定一个初始入口点。 使用–entry命令行选项选择入口点。

posted on   zxddesk  阅读(13)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示