当前位置: 首页 > 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 的物品。…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...