一、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头


  1. #define EI_NIDENT 16
  2. struct Elf32_Ehdr //共52个字节 //Ehdr表示ELF header
  3. {
  4.   unsigned char  e_ident[EI_NIDENT];
  5.   Elf32_Half e_type; //类型包括:可执行文件、可重定向文件、共享目标文件等
  6.   Elf32_Half e_machine; //有X86、arm之类
  7.   Elf32_Word e_version;
  8.   Elf32_Addr e_entry; //可执行程序的入口地址
  9.   Elf32_Off e_phoff; //Program头表的偏移地址
  10.   Elf32_Off e_shoff; //Section头表的偏移地址
  11.   Elf32_Word e_flags;
  12.   Elf32_Half e_ehsize; //本结构体的size
  13.   Elf32_Half e_phentsize; //单个Program头的size
  14.   Elf32_Half e_phnum; //Segment头表中Segment头的个数
  15.   Elf32_Half e_shentsize; //单个Section头的szie
  16.   Elf32_Half e_shnum; //Section头表中Section头的个数
  17.   Elf32_Half e_shstrndx; //储存Section名字集合的Section的下标,指".shstrtab"的下标
  18. };

2)Section头


  1. struct Elf32_Shdr //共40个字节 //Shdl表示Section header
  2. {
  3.     Elf32_Word sh_name; //所指向Section的名字,如".text"、".data"、".bss"等
  4.     Elf32_Word sh_type; //所指向Section的类型,如:符号表、字符串表等
  5.     Elf32_Word sh_flags;
  6.     Elf32_Addr sh_addr;
  7.     Elf32_Off sh_offset; //所指向Section在ELF文件中的偏移量
  8.     Elf32_Word sh_size; //所指向Section的size
  9.     Elf32_Word sh_link; //和其关联的Section头的下标索引
  10.     Elf32_Word sh_info;
  11.     Elf32_Word sh_addralign; //字节对齐
  12.     Elf32_Word sh_entsize;
  13. };

3)Program头


  1. struct Elf32_phdr //32个字节 //phdr表示Program header
  2. {
  3.     Elf32_Word p_type; //如PT_LOAD表示,对应Segment可被加载到内存中
  4.     Elf32_Off p_offset; //Segment在ELF文件中的偏移量
  5.     Elf32_Addr p_vaddr; //Segment映射到内存后的虚拟地址
  6.     Elf32_Addr p_paddr; //Segment映射到内存后的物理地址,此时与虚拟地址相同
  7.     Elf32_Word p_filesz; //Segment在ELF文件中占用的size
  8.     Elf32_Word p_memsz; //Segment映射到内存后占用的size
  9.     Elf32_Word p_flage; //读、写、执行权限
  10.     Elf32_Word p_align; //字节对齐,p_vaddr和p_paddr对p_align取模后为0
  11. };

更详细内容请参考: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有如下定义:


  1. struct Elf32_sym //
  2. {
  3.     Elf32_Word st_name; //符号的名字
  4.     Elf32_Addr st_value; //符号相对于其所在Section偏移的相对地址
  5.     Elf32_Word st_size; //符号的size
  6.     unsigned char st_info; //低四位表示符号的作用范围(全局或局部),高四位表示符号的类型(变量、函数等)
  7.     unsigned char st_other;
  8.     Elf32_Half st_shndx; //该符号的值在哪个Section下存储
  9. };

实例:

以上图中的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)堆栈溢出

参考资料:

1.ELF文件解析和加载(附代码)

2.ELF文件格式解析(完)

3.ELF文件详解—初步认识

</article>

[转帖]ELF文件详解的更多相关文章

  1. Linux环境下:程序的链接, 装载和库[ELF文件详解]

    编译过程拆解 预处理处理生成.i文件, .i文件还是源码文件 将所有的宏定义#define展开. 处理#if, #else, #endif等条件编译指令 处理#include, 原地插入文件 cpp ...

  2. ELF文件解析(二):ELF header详解

    上一篇讲了ELF文件的总体布局,以及section和segment的概念.按照计划,今天继续讲 ELF header. 讲新的内容之前,先更正一个错误:上一篇中讲section header tabl ...

  3. Angular Npm Package.Json文件详解

    Angular7 Npm Package.Json文件详解   近期时间比较充裕,正好想了解下Angular Project相关内容.于是将Npm官网上关于Package.json的官方说明文档进行了 ...

  4. Android逆向之旅---SO(ELF)文件格式详解(转)

    第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...

  5. Android逆向之旅---SO(ELF)文件格式详解

    第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...

  6. web.xml文件详解

      web.xml文件详解 Table of Contents 1 listener. filter.servlet 加载顺序 2 web.xml文件详解 3 相应元素配置 1 listener. f ...

  7. Linux中/proc目录下文件详解

    转载于:http://blog.chinaunix.net/uid-10449864-id-2956854.html Linux中/proc目录下文件详解(一)/proc文件系统下的多种文件提供的系统 ...

  8. SUBLIME TEXT 2 设置文件详解

    SUBLIME TEXT 2 设置文件详解 Preferences.sublime-settings文件: // While you can edit this file, it’s best to ...

  9. [转]AndroidManifest.xml文件详解

    转自:http://www.cnblogs.com/greatverve/archive/2012/05/08/AndroidManifest-xml.html AndroidManifest.xml ...

  10. delphi 资源文件详解

    delphi资源文件详解 一.引子: 现在的Windows应用程序几乎都使用图标.图片.光标.声音等,我们称它们为资源(Resource).最简单的使用资源的办法是把这些资源的源文件打入软件包,以方便 ...

随机推荐

  1. FlinkSQL实战开发

    FlinkSQL实战开发 1.基础知识 FlinkSQL分为Table API和SQL API,是架构于Flink Core之上用SQL予以方便快捷地进行结构化数据处理的上层库. 工作流程 SQL和T ...

  2. 【华为云技术分享】STM32L476移植华为LiteOS系列教程(二)---开发前准备

    在进行移植华为LiteOS开发工作之前,我们是需要提前做一些准备工作,如:开发工具.环境.源码等相关事宜. 一.准备开发工具 STM32CubeMX用于生成工程文件:STM32CubeMX下载地址 I ...

  3. SARIF:DevSecOps工具与平台交互的桥梁

    摘要:静态扫描工具融入在DevSecOps的开发过程中,对提高产品的整体的安全水平发挥着重要的作用.为了获取安全检查能力覆盖的最大化,开发团队通常会引入多个安全扫描工具.为了降低各种分析工具的结果汇总 ...

  4. 火山引擎DataLeap背后的支持者 - 工作流编排调度系统FlowX

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景介绍 业务场景 在日常工作中,我们时不时需要对某些逻辑进行重复调度,这时我们就需要一个调度系统.根据不同的调度 ...

  5. 人工智能聊天DEMO

    import urllib.parse import requests #调用机器人接口 def qingyunke(msg): url = "http://api.qingyunke.co ...

  6. python 解析字节码的相关方法

    python代码被解释器执行时分为两步走: 一.python编译器将代码编译成字节码 二.python虚拟机执行字节码 由于这两步是一起的,所以在python编程中很少能看到字节码.但是想要提高代码效 ...

  7. 打破虚拟边界的视频交互新方式,AR隔空书写的应用理念和探索实践

    AR隔空书写演示 随着技术的发展和超视频化的时代驱动,交互的形式日渐丰富.从屏幕点触,到语音交互,人脸.指纹.声纹,再到近年流行的AR和VR--人类早在语言出现之前便习惯使用肢体和手势这种近乎本能的沟 ...

  8. 2019年第十届蓝桥杯国赛C++C组

    蓝桥杯历年国赛真题汇总:Here 统一声明 如果不写默认带有常用头文件 如果不表明主函数默认表示在 void solve(){} 默认使用 using namespace std; ios::sync ...

  9. 关于 Serverless 应用架构对企业价值的一些思考

    作者:寒斜 前言 对于企业方而言,最关心的核心诉求就是如何能获取更多的营收,更高的利润,通俗点说就是如何赚更多的钱:企业赚钱的方式主要是通过出售企业服务,当用户购买更多的企业服务,企业赚的钱就越多:而 ...

  10. vue.js从输入中的contenteditable元素获取innerhtml

    <div class="actual-score" :contenteditable="$route.params.mode === 'edit'" v- ...