[转帖]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).最简单的使用资源的办法是把这些资源的源文件打入软件包,以方便 ...
随机推荐
- 如何将没有复制或移动构造函数的对象放入vector容器
正文 直接说答案,这个问题无法实现.原因是因为std::vector容器的插入一定会调用类对象的构造函数或者移动构造函数. 说一下为什么会有这个问题,因为不想用指针,我想直接通过类对象本身的RAII机 ...
- Nignx快速入门
Nginx快速入门 一.简介 产生的背景:当一台服务器同一时刻被大量客户端请求访问时,访问量超出服务器请求范围,服务器处理不过来,发生宕机或者丢失连接情况下,产生了Nignx反向代理技术. Nginx ...
- 创建service后,kubernetes会发生什么
本文分享自华为云社区<当创建一个service后,kubernetes会发生什么?>,作者:可以交个朋友. 一.Service介绍 1.1 Kubernetes为什么会引入service? ...
- 十八般武艺玩转GaussDB(DWS)性能调优(二):坏味道SQL识别
摘要:那些会导致执行效率低下的SQL语句及其执行方式,我们称之为SQL中的"坏味道". ◆ 什么是SQL中的坏味道 SQL语言是关系型数据库(RDB)的标准语言,其作用是将使用者的 ...
- 云图说|移动应用安全服务—App的体检中心,全面检测,安全上路!
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 移动应用安全服务 ...
- 应用架构步入“无服务器”时代 Serverless技术迎来新发展
摘要:以"原生蓄力,云领未来"为主题的2021年云原生产业大会上,华为云Serverless函数服务产品经理分享了"华为云Serverless函数服务,让开发上云极简高效 ...
- 玩转LiteOS组件:Openexif
摘要:OpenExif是用于访问Exif格式的JPEG图像文件的面向对象的库. 本文分享自华为云社区<LiteOS组件尝鲜-玩转Openexif>,作者: W922 . 基本信息 Exif ...
- 24校招,Moka测试开发工程师一面
前言 大家好,今天回顾一下楼主当时参加moka测试开发工程师的面试 对其中一些重要问题,我也给出了相应的答案 过程 自我介绍 挑一个项目,详细介绍你在其中担任的职责 如何安排工作的,有什么成果? 回归 ...
- DevEco中被忽略的实用功能
近期,我一直在业余时间研究纯血鸿蒙(HarmonyOS)App的开发,所使用的IDE是华为官方的DevEco Studio 3.1.1. 随着使用时间的增长,我发现了几个特别容易被忽略,但又特别实用的 ...
- AtCoder Beginner Contest 185 题解
A - ABC Preparation 排序找出最小值 int main() { ios_base::sync_with_stdio(false), cin.tie(0); vector<int ...