一、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. 又双叒叕种草了新家装风格?AI帮你家居换装

    摘要:又双叒叕种草了家装新风格?想要尝试却又怕踩雷?如果能够轻松Get量身定制的家装风格图,那该多好啊.现在,这一切都成为了可能! 本文分享自华为云社区<又双叒叕种草了新家装风格?AI帮你家居换 ...

  2. CANN AICPU算子耗时分析及优化探索

    摘要:本文以GreaterEqual作为测试算子,该算子计算逻辑较为简单(output = input1 >= input2),旨在尽可能降低计算耗时,使得算子耗时尽可能以数据操作和算子调度作为 ...

  3. # github.com/coreos/etcd/clientv3/balancer/resolver/endpoint

    linux使用go连接etcd集群时报错: # github.com/coreos/etcd/clientv3/balancer/resolver/endpoint /root/go/pkg/mod/ ...

  4. C# 内存缓存工具类 MemoryCacheUtil

    C# 内存缓存工具类 MemoryCacheUtil using System; using System.Collections.Concurrent; using System.Collectio ...

  5. 【每日一题】20.K-th Number (二分 + 尺取)

    关于此题,我们分析一下: 一个区间第k大的数不小于x的条件是什么? 答案就是一个区间内不小于x的数的个数不小于k 那么,我们就会发现,我们其实并不需要知道每个数的值,实际上对我们有用的只有每个数与x的 ...

  6. MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差

    之前已经分享过多篇关于Spring Boot中使用Java 21新特性虚拟线程的性能测试案例: Spring Boot 3.2虚拟线程搭建静态文件服务器有多快? Spring Boot 虚拟线程与We ...

  7. vue3调用高德地图,实现地址,经纬度填写

    父组件引用高德地图: 1 <template> 2 <div class="wrapper"> 3 <div class="box" ...

  8. VueTreeselect

    https://www.vue-treeselect.cn/ 官网简介

  9. mysql批量替换字段

    一.需求将数据库内指定的数据进行批量修改,可使用replace函数如IP:端口号,只改端口号的部分,将所有192.168.1.1:8001的数据8001的端口号修改为3001replace函数的用法如 ...

  10. 运筹学 | 退化的最优解 vs 无穷多最优解?

    退化的最优解: 单纯形表的基可行解中,出现等于零的基变量.或者,按最小比值来确定出基向量时,存在两个以上相同最小比值. 出现的原因:模型中存在多余的约束. 无穷多最优解: 单纯形表中,按最大检验数 σ ...