[转帖]ELF文件详解
一、ELF概述
1、ELF的定义
ELF(Executable and Linkable Format)文件是一种目标文件格式,常见的ELF格式文件包括:可执行文件、可重定位文件(.o)、共享目标文件(.so)、核心转储文件等。
ELF主要用于Linux平台,Windows下是PE/COFF格式。
2、ELF文件的结构
一个完整的ELF文件一般会包括如下几个内容:ELF头、Section头、Program头和Section。
其中由Section头组成的集合称为Section头表,由Program头组成的集合称为Program头表。注意:数个连续的头称之为头表,头表是虚拟出来的定义,文件中不存在头表,只有头。
一个Section头指向一个Section,Section头中包括所指向Section的名字、类型、其在ELF文件中的偏移地址、大小等信息。
一个Program头指向一个Segment,Program头中包括所指向Segment的类型、其在ELF文件中的偏移地址、大小,映射到内存的虚拟地址等信息。一个Segment由一系列连续的Section构成,连续的Section拥有相同的权限,如只读、读写、可读可执行等;
一个ELF头内包含有:Section头表的在ELF文件中的偏移地址、单个Section头的大小、Section头表中Section头的个数;Program头表的在ELF文件中的偏移地址、单个Program头的大小、Program头表中Program头的个数;该ELF文件的类型,若是可执行文件的话,还包含的有程序的入口地址。
3、头的表示方法及其含义
1)变量及其大小:

2)ELF头
-
#define EI_NIDENT 16
-
-
struct Elf32_Ehdr //共52个字节 //Ehdr表示ELF header
-
{
-
unsigned char e_ident[EI_NIDENT];
-
Elf32_Half e_type; //类型包括:可执行文件、可重定向文件、共享目标文件等
-
Elf32_Half e_machine; //有X86、arm之类
-
Elf32_Word e_version;
-
Elf32_Addr e_entry; //可执行程序的入口地址
-
Elf32_Off e_phoff; //Program头表的偏移地址
-
Elf32_Off e_shoff; //Section头表的偏移地址
-
Elf32_Word e_flags;
-
Elf32_Half e_ehsize; //本结构体的size
-
Elf32_Half e_phentsize; //单个Program头的size
-
Elf32_Half e_phnum; //Segment头表中Segment头的个数
-
Elf32_Half e_shentsize; //单个Section头的szie
-
Elf32_Half e_shnum; //Section头表中Section头的个数
-
Elf32_Half e_shstrndx; //储存Section名字集合的Section的下标,指".shstrtab"的下标
-
};
2)Section头
-
struct Elf32_Shdr //共40个字节 //Shdl表示Section header
-
{
-
Elf32_Word sh_name; //所指向Section的名字,如".text"、".data"、".bss"等
-
Elf32_Word sh_type; //所指向Section的类型,如:符号表、字符串表等
-
Elf32_Word sh_flags;
-
Elf32_Addr sh_addr;
-
Elf32_Off sh_offset; //所指向Section在ELF文件中的偏移量
-
Elf32_Word sh_size; //所指向Section的size
-
Elf32_Word sh_link; //和其关联的Section头的下标索引
-
Elf32_Word sh_info;
-
Elf32_Word sh_addralign; //字节对齐
-
Elf32_Word sh_entsize;
-
};
3)Program头
-
struct Elf32_phdr //32个字节 //phdr表示Program header
-
{
-
Elf32_Word p_type; //如PT_LOAD表示,对应Segment可被加载到内存中
-
Elf32_Off p_offset; //Segment在ELF文件中的偏移量
-
Elf32_Addr p_vaddr; //Segment映射到内存后的虚拟地址
-
Elf32_Addr p_paddr; //Segment映射到内存后的物理地址,此时与虚拟地址相同
-
Elf32_Word p_filesz; //Segment在ELF文件中占用的size
-
Elf32_Word p_memsz; //Segment映射到内存后占用的size
-
Elf32_Word p_flage; //读、写、执行权限
-
Elf32_Word p_align; //字节对齐,p_vaddr和p_paddr对p_align取模后为0
-
};
更详细内容请参考:ELF文件格式解析
4、实例解析
可执行文件中Program头表是必须的,可重定向文件(.o)中Section头表是必须的,共享目标文件(.so)中两者都是必须的。
1)可重定向文件分析
ELF头信息如下所示:
在此文件中,可看到其类型为REL, 即可重定向文件。其中Program头的个数为0,Section头的个数为8个,没有程序入口地址。
下图是8个Section头的详细信息:
其中Addr在此处被填充为了0的原因是,其目前并不需要被加载到内存中,在链接的时候才会被填充。
根据上述各Section的偏移量及size可推断出其在该可重定向文件中空间布局,如下表所示:
| 偏移量(Off) | 大小(size) | Section | 备注 |
| 0x0 | 0x34 | ELF头 | 0x34表示十进制的52,刚好为ELF头的大小 |
| 0x34 | 0x2a | .text | |
| 0x60 | 0x38 | .data | |
| 0x98 | 0x0 | .bss | |
| 0x98 | 0x30 | .shstrtab | |
| 0xc8 | 0x140 | Section头表 | 一个Section头的大小为40个字节,共8个头,大小为0x140 |
| 0x208 | 0x80 | .symtab | |
| 0x288 | 0x28 | .strtab | |
| 0x2b0 | 0x10 | .rel.text |
下面详述上面几种类型的Section:
Ⅰ .shstrtab
.shstrtab中存放着各个Section的名字。
Ⅱ .strtab
.symtab中存放着程序中用到的符号的名字。
Ⅲ .bss
程序中未初始化的全局变量都会被归类到bss段,并在程序加载的时候被初始化为0。
在加载.bss的时候和.data一样,都属于可读可写的数据,但在ELF文件中.data需要占用一段内存空间来保存变量的初始化值,而.bss却不需要。
也就是说,.bss只占用一个Section头的大小,而不需要对应的Section。如上表中可以看出.bss所描述Section的size为0。
Ⅳ .rel.text
.rel.text用于告诉链接器,哪些地方需要重定向。
Ⅴ .symtab
.symtab内存放着程序中用到的符号,包括变量符号、函数符号,如printf、main等。
.symtab有如下定义:
-
struct Elf32_sym //
-
{
-
Elf32_Word st_name; //符号的名字
-
Elf32_Addr st_value; //符号相对于其所在Section偏移的相对地址
-
Elf32_Word st_size; //符号的size
-
unsigned char st_info; //低四位表示符号的作用范围(全局或局部),高四位表示符号的类型(变量、函数等)
-
unsigned char st_other;
-
Elf32_Half st_shndx; //该符号的值在哪个Section下存储
-
};
实例:

以上图中的data_items为例,其Ndx为3,表示其在第3个Section,即.data。data_items的value值为00000000,表示其相对于.data的偏移地址为0,即data_itms在.data的开头。
_start的value也为00000000,表示其在.text的开头,也即整个代码的入口是_start。
2)可执行文件分析
可执行文件的ELF头信息如下所示:
相对于可重定向文件来说,其类型变为了EXEC,少了两个Section header,多了两个Program头,并且有可执行程序的入口地址。
6个Section头如下所示:
从图中可以看出,.text和.data的Addr不再为0,有了实际的值,这便是在链接过程中装载上的。
.bss段因为没有使用到,所以被删除掉了。
.rel.text在链接之后,便完成了自己的使命,也就被删除掉了。
根据上述各Section的偏移量及size可推断出其在该可执行文件中空间布局,如下表所示:
| 偏移量(Off) | 大小(size) | Section | 备注 |
| 0x0 | 0x34 | ELF头 | 0x34表示十进制的52,刚好为ELF头的大小 |
| 0x34 | 0x40 | Program头表 | 一个Program头的大小为32字节,共2个头,大小为0x40 |
| 0x74 | 0x2a | .text | |
| 0xa0 | 0x38 | .data | |
| 0xd8 | 0x27 | .shstrtab | |
| 0x100 | 0xf0 | Section头表 | 一个Section头的大小为40个字节,共6个头,大小为0xf0 |
| 0x1f0 | 0xa0 | .symtab | |
| 0x290 | 0x40 | .strtab |
2个Program头如下所示:
结合Program头和Section的空间布局表可以看出,ELF头、Program头表和Section头表共同组成了第一个Segment;.data单独组成了另一个Segment。
VirtAddr列指出第一个Segment加载到虚拟地址0x0804 8000(注意在x86平台上后面的PhysAddr列是没有意义的),第二个Segment加载到地址0x0804 90a0。
Flg列指出第一个Segment的访问权限是可读可执行,第二个Segment的访问权限是可读可写。
最后一列Align的值0x1000(4K)是x86平台的内存页面大小。在加载时要求文件中的一页对应内存中的一页,对应关系如下图所示:
这个可执行文件很小,总共也不超过一页大小,但是两个Segment必须加载到内存中两个不同的页面,因为MMU的权限保护机制是以页为单位的,一个页面只能设置一种权限。
此外还规定每个Segment在文件页面内偏移多少加载到内存页面仍然偏移多少,比如第二个Segment在文件中的偏移是0xa0,在内存页面0x0804 9000中的偏移仍然是0xa0,所以是从0x0804 90a0开始,这样规定是为了简化链接器和加载器的实现。
从上图也可以看出.text段的加载地址应该是0x0804 8074,这也正是程序的入口地址。
原来目标文件符号表中的Value都是相对地址,现在都改成绝对地址了。此外还多了三个符号__bss_start、_edata和_end,这些是在链接过程中添进去的,加载器可以利用这些信息把.bss段初始化为0。
5、可执行ELF文件的装载过程
附录
1)Section与Segment的英文含义
section和segment都有部分的意思,但
section指的“部分”是不同质的,如:The TOEFL is divided into three sectiond, namely listening, structure and reading.在这里托福考试是由三部分组成的,这三部分是不一样的,即不同质的。
而segment指的“部分”是同质的,如:I want the middle segment of the rope. 我想要中间那段绳...
2)段错误(Segment Error)
当程序试图访问不允许访问的内存位置,或试图以不允许的方式访问内存位置(例如尝试写入只读位置,或覆盖部分操作系统)时会发生段错误。
常见的段错误,包括:
1)使用未经初始化及或已经释放的指针地址
2)访问受系统保护的内存地址
3)写入只读的内存地址
4)数组越界
5)堆栈溢出
参考资料:
</article>
[转帖]ELF文件详解的更多相关文章
- Linux环境下:程序的链接, 装载和库[ELF文件详解]
编译过程拆解 预处理处理生成.i文件, .i文件还是源码文件 将所有的宏定义#define展开. 处理#if, #else, #endif等条件编译指令 处理#include, 原地插入文件 cpp ...
- ELF文件解析(二):ELF header详解
上一篇讲了ELF文件的总体布局,以及section和segment的概念.按照计划,今天继续讲 ELF header. 讲新的内容之前,先更正一个错误:上一篇中讲section header tabl ...
- Angular Npm Package.Json文件详解
Angular7 Npm Package.Json文件详解 近期时间比较充裕,正好想了解下Angular Project相关内容.于是将Npm官网上关于Package.json的官方说明文档进行了 ...
- Android逆向之旅---SO(ELF)文件格式详解(转)
第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...
- Android逆向之旅---SO(ELF)文件格式详解
第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...
- web.xml文件详解
web.xml文件详解 Table of Contents 1 listener. filter.servlet 加载顺序 2 web.xml文件详解 3 相应元素配置 1 listener. f ...
- Linux中/proc目录下文件详解
转载于:http://blog.chinaunix.net/uid-10449864-id-2956854.html Linux中/proc目录下文件详解(一)/proc文件系统下的多种文件提供的系统 ...
- SUBLIME TEXT 2 设置文件详解
SUBLIME TEXT 2 设置文件详解 Preferences.sublime-settings文件: // While you can edit this file, it’s best to ...
- [转]AndroidManifest.xml文件详解
转自:http://www.cnblogs.com/greatverve/archive/2012/05/08/AndroidManifest-xml.html AndroidManifest.xml ...
- delphi 资源文件详解
delphi资源文件详解 一.引子: 现在的Windows应用程序几乎都使用图标.图片.光标.声音等,我们称它们为资源(Resource).最简单的使用资源的办法是把这些资源的源文件打入软件包,以方便 ...
随机推荐
- 又双叒叕种草了新家装风格?AI帮你家居换装
摘要:又双叒叕种草了家装新风格?想要尝试却又怕踩雷?如果能够轻松Get量身定制的家装风格图,那该多好啊.现在,这一切都成为了可能! 本文分享自华为云社区<又双叒叕种草了新家装风格?AI帮你家居换 ...
- CANN AICPU算子耗时分析及优化探索
摘要:本文以GreaterEqual作为测试算子,该算子计算逻辑较为简单(output = input1 >= input2),旨在尽可能降低计算耗时,使得算子耗时尽可能以数据操作和算子调度作为 ...
- # github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
linux使用go连接etcd集群时报错: # github.com/coreos/etcd/clientv3/balancer/resolver/endpoint /root/go/pkg/mod/ ...
- C# 内存缓存工具类 MemoryCacheUtil
C# 内存缓存工具类 MemoryCacheUtil using System; using System.Collections.Concurrent; using System.Collectio ...
- 【每日一题】20.K-th Number (二分 + 尺取)
关于此题,我们分析一下: 一个区间第k大的数不小于x的条件是什么? 答案就是一个区间内不小于x的数的个数不小于k 那么,我们就会发现,我们其实并不需要知道每个数的值,实际上对我们有用的只有每个数与x的 ...
- MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差
之前已经分享过多篇关于Spring Boot中使用Java 21新特性虚拟线程的性能测试案例: Spring Boot 3.2虚拟线程搭建静态文件服务器有多快? Spring Boot 虚拟线程与We ...
- vue3调用高德地图,实现地址,经纬度填写
父组件引用高德地图: 1 <template> 2 <div class="wrapper"> 3 <div class="box" ...
- VueTreeselect
https://www.vue-treeselect.cn/ 官网简介
- mysql批量替换字段
一.需求将数据库内指定的数据进行批量修改,可使用replace函数如IP:端口号,只改端口号的部分,将所有192.168.1.1:8001的数据8001的端口号修改为3001replace函数的用法如 ...
- 运筹学 | 退化的最优解 vs 无穷多最优解?
退化的最优解: 单纯形表的基可行解中,出现等于零的基变量.或者,按最小比值来确定出基向量时,存在两个以上相同最小比值. 出现的原因:模型中存在多余的约束. 无穷多最优解: 单纯形表中,按最大检验数 σ ...