当前位置: 首页 > news >正文

Linux- 浅谈ELF目标文件格式

        理解了进程的描述和创建之后,自然会想到我们编写的可执行程序是如何作为一个进程工作的?这就涉及可执行文件的格式、编译、链接和装载等相关知识。

        这里先提一个常见的名词“目标文件”,是指编译器生成的文件“目标”指目标平台, 例如 x86 或 x86-64,它决定了编译器使用的机器指令集。目标文件一般也叫作 ABI(Application Binary Interface,应用程序二进制接口),目标文件和目标平台是二进制兼容的。二进制兼容 即指该目标文件已经是适应某一种 CPU 体系结构上的二进制指令。例如一个编译出来的x86-64目标文件是无法链接成ARM上的可执行文件的。

        最古老的目标文件格式是 a.out,后来发展成 COFF,现在常用的有 PE (Windows)和 ELF(Linux)。

        ELF(Executable and Linkable Format)即可执行的和可链接的格式, 是一个文件格式的标准。ELF 格式的文件用于存储 Linux 程序。ELF 是一 种对象文件的格式,用于定义不同类型的对象文件中都有什么内容、以什么样的格式放这些内容ELF 首部会描绘整个文件的组织结构,它还包括很多节(sections,是在 ELF 文件里用以装载内容数据的最小容器),这些节有些是系统定义好的,有些是用户在文件中通过.section 命令自定义的,链接器会将多个输入目标文件中相同的节合并。

我们先来看一个例子,直观感受一下

test.c原文件内容如下:

#include <stdio.h>int g(int x)
{return x + 3;
}int f(int x)
{return g(x);
}int main()
{return f(8) + 1;
}

ELF 文件的 3 种类型

以 ELF 格式为例,来看在可执行文件格式里的 3 种不同类型的目标文件。

(1)可重定位文件:这种文件一般是中间文件,还需要继续处理。由编译器和汇编器创建,一个源代码文件会生成一个可重定位文件。文件中保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者动态链接库文件。在编译 Linux 内核时可能会注意到,每个内核源代码.c 文件都会生成一个同名的.o 文件,该文件即为可重定位目标文件,最后所有.o 文件会链接为一个文件,即 Linux 内核。另外,静态链接库文件实际上就是可重定位文件的打包,也是可重定位文件,一般以.a作为文件名后缀。

(2)可执行文件:一般由多个可重定位文件结合生成,是完成了所有重定位工作和符号解析的文件(动态链接库符号是在运行时解析的),文件中保存着一个用来执行的程序。重定位和符号解析会在链接部分详细介绍。

(3)动态链接库文件:也称为共享目标文件,是已经经过链接处理可以直接加载运行的库文件,是可以被可执行文件或其他动态链接库文件加载使用的库文件,例如标准 C 的库文件 libc.so。可以简单理解为没有主函数 main 的“可执行”文件,只有一堆函数可供其他程序调用。Linux 下动态链接库文件文件名后缀为.so 的文件,so 代表 shared object。

ELF 文件的作用

        ELF 文件参与程序的链接(构建一个可执行程序)和程序的执行(加载可执行程序),所以可以从不同的角度来看待 ELF 格式的文件。

  • 如果用于编译和链接(可重定位文件),则编译器和链接器将把 ELF 文件看作节的集合,所有节由节头表描述,程序头表可选。
  • 如果用于加载执行(可执行文件),则加载器将把 ELF 文件看作程序头表描述的段的集合,一个段可能包含多个节和节头表可选。
  • 如果是动态链接库文件,则两者都含有

ELF文件格式

ELF文件的索引表

        ELF文件的索引表。ELF文件的主体是各种节,典型的如代码节.text,还有描述这些节属性的信息(Program header table和Section header table),以及ELF文件的整体描述信息(ELF header),整体如图所示。

ELF Header结构

        ELF Header在文件最开始描述了该文件的组织情况。ELF文件头会指出可执行文件是32位还是64位的,e_ident数组的第五个字节是1表示是32位,2表示是64位。ELF Header的其他部分主要说明了其他文件内容的位置、大小等信息。ELF Header长度为64字节,在/usr/include/elf.h文件中,可以看到其C语言格式的定义如下:

        ELF表头首先会给出很多关于本ELF文件的属性信息,如上面提及到的3种ELF类型就是通过e_type来体现的。e_type的值1,2,3,4分别代表可重定位目标文件,可执行文件,共享目标文件和核心文件。如上面的ELF文件索引表所示,其中最重要的是段头表(Program header table)和节头表(Section header table)的位置。

        段头表存储于文件的e_phoff(ELF header的字段,下同)位置,有e_phnum项内容,每项大小为e_phentsize字节。

        节头表基本定义了整个ELF文件的组成,可以说是整个ELF就是由若干个节(Section)组成的。段只是对节区进行了重新组合,将连续的多个节区描述为一段连续区域,对应到一块连续的内存地址中。

Section Header结构

        节头表是由Section Header组成的表,包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件有没有这个表都可以。每个节区头部结构的描述如下:

我们可以查看hello这个可执行文件的sections、指令及主要输出内容如下节区信息。前6列分别是[Nr]索引、Name 节名、Type 类型、Addr 虚拟地址、Off 偏移和Size 节大小。简单来说,该节描述了将可执行文件中起始位置位Off、大小为Size的一段数据加载到内存地址Addr。

(ps:hello.c文件中只是单纯的打印了一句"Hello World!") 

我们以"[6].text"来理解每一节头的内容(一行是一个节的描述)。

  • Type PROGBITS表示该节存储的是代码。
  • Addr为080490a0 是该部分将加载到内存中的虚拟地址。
  • Off为节在可执行文件中的偏移。
  • 后半部分的Key to Flags是对Flg中标识的说明。如.text节Flg为AX,A(Alloc)表示需要加载到内存中,X(eXecute)表示对应内存需要可执行权限。

Program Header结构

        段头(Program Header)表是和创建进程相关的,描述了连续的几个节在文件中的位置、大小以及它被放进内存后的位置和大小,告诉系统如何创建进程映像,可执行文件加载器就可以按这个说明将可执行文件搬到内存中。用来构造进程映像的目标文件必须具有段头表,可重定位文件不需要这个表。

我们可以查看生成的hello这个可执行文件的段头表,指令及主要输出内容如下段头表示例。

8列分别是Type类型、Offset文件偏移、VirtAddr虚拟地址、PhysAddr物理地址、FileSiz可执行文件中该区域的大小、MemSiz内存中该区域的大小、Flg属性标识和Align对齐方式。和节头表相似,该表描述了可执行文件中起始位置为Offset、大小为FileSiz的一段数据,加载到内存地址VirtAddr中。两者的虚拟地址信息是一致的,但节头表的Addr可以没有信息,可重定位目标文件的Addr就是全0。 

我们以第一行为例进行说明,Type值为LOAD表示该段(Segment)需要加载到内存,Offset全0表示其内容为从可执行文件头开始共0x001e8(FileSiz)个字节,加载到虚拟地址0x08048000(VirtAddr)处,该段为可读(R)权限,4k(Align,0x1000)对齐。

再往下看为节与段的映射关系说明(Section to Segment mapping:),00即第一行描述的段,一共包括了.note、.gnu、.ABI-tag等多个节。

相关操作指令

我们可以使用如下指令对ELF进行更多的研究实践。

(1)man elf:在Linux下输入此指令即可查看详细的格式定义。

(2)readelf:用于显示一个或多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。

  • -a    等价于 -h -l -S -s -r -d -V -A - I
  • -h    显示elf文件开始的文件头信息
  • -S    显示节头信息(如果有)
  • -l      显示Program Header。(小写的L)
  • -s     显示符号表段中的项(如果有)
  • -r      显示可重定位段的信息
  • -H     显示readelf所支持的命令行选项

(3)objdump:显示二进制文件信息,用于查看目标文件或者可执行的目标文件的构成的gcc工具,选项如下。

  • -f     显示objfile中每个文件的整体头部摘要信息
  • -h    显示目标文件各个section的头部摘要信息
  • -r     显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来
  • -s    显示指定section的完整内容。默认所有的非空section都会被显示
  • -t     显示文件的符号表入口
  • -x     显示所可用的头信息,包括符号表、重定位入口。-x等价于-a -f -h -r -t同时指定

(4)hexdump:用十六进制的数字来显示elf的内容。


以上内容为中科大软件学院《Linux操作系统分析》课后总结,感谢孟宁老师的倾心教授,老师讲的太好啦(^_^)

参考资料:《庖丁解牛Linux内核分析》    孟宁  编著

相关文章:

Linux- 浅谈ELF目标文件格式

理解了进程的描述和创建之后&#xff0c;自然会想到我们编写的可执行程序是如何作为一个进程工作的&#xff1f;这就涉及可执行文件的格式、编译、链接和装载等相关知识。 这里先提一个常见的名词“目标文件”&#xff0c;是指编译器生成的文件。“目标”指目标平台&#xff0c…...

C++ MVC模式

概述 C是一种流行的编程语言&#xff0c;它可以用于构建各种类型的应用程序&#xff0c;包括Web应用程序、桌面应用程序和移动应用程序。在这里&#xff0c;我将为您介绍C中的MVC模式&#xff0c;以及如何在C中实现MVC模式。 MVC&#xff08;Model-View-Controller&#xff0…...

IntelliJ IDEA2021安装教程

1.鼠标右击【JetBrains 2021】压缩包&#xff08;win11系统需先点击“显示更多选项”&#xff09;选择【解压到“JetBrains 2021”】 2.打开解压后的文件夹&#xff0c;鼠标右击您需要安装的软件名称&#xff08;如&#xff1a;IdealU-2021.3.1&#xff09;选择【以管理员身份运…...

day16—选择题

文章目录1.计算每位学生的多学科加总成绩的SQL是&#xff08;C&#xff09;2.以下哪个不是与Mysql服务器相互作用的通讯协议&#xff08;B&#xff09;3.设有两个事务T1,T2,其并发操作如下所示,下面评价正确的是&#xff08;D&#xff09;4.如果事务T获得了数据项Q上的排它锁&a…...

LLVM 的中间代码(IR) 基本语法

LLVM 的中间代码&#xff08;IR&#xff09; 基本语法 以下是 LLVM IR 的基本语法和每个语法的实例代码&#xff1a; 1.数据类型 LLVM IR 支持多种数据类型&#xff0c;包括整型、浮点型、指针型和向量型等。以下是 LLVM IR 中定义不同类型的语法和示例代码&#xff1a; 整…...

多标签在单行出现省略的实现

简言 实现在有宽度的一行内&#xff0c;标签过多会出现省略号。 实现 首先要实现单行省略的效果。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&quo…...

第二十七章 纹理总结

OpenGL 纹理是左下角(0,0) 右上角(1,1)。 需要告诉OpenGL纹理环绕方式,主要有四种:GL_REPEAT(重复纹理图像),GL_MIRRORED_REPEAT(重复纹理图像,但是每次重复图片是镜像放置的), GL_CLAMP_TO_EDGE(坐标再0-1直接,超出部分会重复纹理坐标的边缘,有边缘拉伸效果),GL…...

【Linux面试】-(腾讯,百度,美团,滴滴)

文章目录Linux 面试题-(腾讯,百度,美团,滴滴) 分析日志 t.log(访问量)&#xff0c;将各个 ip 地址截取&#xff0c;并统计出现次数,并按从大到小排序(腾讯) http://192.168.200.10/index1.html http://192.168.200.10/index2.html http://192.168.200.20/index1.html http://19…...

编译链接和预处理

&#x1f33a;在本次的博客当中我们可以一起来学习一下C语言的编译链接以及预处理的知识。 &#x1f33a;要谈到C语言的编译和链接就肯定得从我们C语言程序运行的时候所产生的诸多的文件类型了。我们正常情况下编辑书写代码的文件为 .c 文件&#xff0c;我们的 .c 文件通过遍历…...

基于sprinmgboot实现实习管理系统的设计【源码+论文】

基于sprinmgboot实习管理系统的设计与实现演示摘要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;实习管理也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管…...

动态规划入门

前言&#xff1a;首先&#xff0c;什么是动态规划&#xff1f; 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。动态规划算法与分治法类似&#xff0c;其基本思…...

day26 SpringBootWeb案例(二)

目录 SpringBootWeb案例 1. 新增员工 1.1 需求 1.2 开发 1.3 测试 2. 文件上传 2.1 简介 2.2 本地存储 2.3 阿里云OSS 3. 配置文件 3.1 Value 3.2 yml配置 3.3 ConfigurationProperties 4. 修改员工 4.1 需求 4.2 查询回显 4.3 保存修改 SpringBootWeb案例 前…...

力扣-《剑指offer》-哈希表-刷题笔记

目录 第一题&#xff1a;03.数组中重复的数字 第二题&#xff1a;39.数组中出现次数超过一半的数字 第三题&#xff1a;50.第一个只出现一次的字符 第四题&#xff1a;53. &#xff08;0-n-1&#xff09;中缺失的数字 第一题&#xff1a;03.数组中重复的数字 我的答案&…...

【SpringBoot】| 邮箱发送验证码,你会了吗?

目录&#x1f981; 题外话&#x1f981; 提前准备2.1 配置邮箱第三方登录2.1.1 点击设置——账户2.1.2 开启POP3/SMTP服务2.2 添加依赖2.3 yaml配置&#x1f981; 进入主题&#x1f981; 测试使用&#x1f981; 尾声3.1 安利一个生成验证码的工具类3.1.1 添加依赖3.1.2 编写配置…...

Linux系统安装部署及配置Grafana

TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…...

Python3 入门教程||Python3 输入和输出||Python3 File 方法

Python3 输入和输出 在前面几个章节中&#xff0c;我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python 两种输出值的方式: 表达式语句和 print() 函数。(第三种方式是使用文件对象的 write() 方法; 标准输出文件可以…...

有效的字母异位词(力扣刷题)

给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。 …...

73、介绍下 HashMap 的底层数据结构

73、介绍下 HashMap 的底层数据结构 我们现在用的都是 JDK 1.8&#xff0c;底层是由“数组链表红黑树”组成&#xff0c;如下图&#xff0c;而在 JDK 1.8 之前是由“数组链表”组成。 1.Hash Hash叫做”散列表“&#xff0c;就是把任意长度的输入&#xff0c;通过散列算法&am…...

系统集成路由器OSPF动态、综合路由配置

实验任务&#xff1a;动态路由协议RIP、OSPF协议的内容和特点动态路由RIP、OSPF实验&#xff0c;建立拓扑pc1>>R1>>R2>>R3>>pc2&#xff0c;使pc1与pc2能相互通信&#xff0c;并配置PC端静默接口。熟悉配置vlan间路由技术&#xff1a;多层交换机虚拟接…...

【力扣周赛 338】

6354. K 件物品的最大和 - 力扣&#xff08;Leetcode&#xff09;袋子中装有一些物品&#xff0c;每个物品上都标记着数字 1、0或 -1。给你四个非负整数 numOnes、numZeros、numNegOnes和 k。袋子最初包含&#xff1a;numOnes 件标记为 1 的物品。numZeroes 件标记为 0 的物品。…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...