[转帖]ELF文件详解

elf,文件,详解 · 浏览次数 : 0

小编点评

**ELF文件解析和加载** **1.ELF文件类型** * REL:可重定向文件 * EXEC:可执行文件 **2.可执行文件的ELF头信息** * Program头表:包含第一个Segment的地址 * Section表:包含所有Segment的地址 **3.可执行文件的 Section信息** * 程序头:包含第一个Segment的地址 * Section:包含所有Segment的地址 **4.可执行文件的加载过程** * 附录 1:包含Section与Segment的英文含义 **5.可执行文件的段错误** * 1)使用未经初始化及或已经释放的指针地址 * 2)访问受系统保护的内存地址 * 3)写入只读的内存地址 * 4)数组越界 * 5)堆栈溢出 **6.常见段错误** * 1)使用未经初始化及或已经释放的指针地址 * 2)访问受系统保护的内存地址 * 3)写入只读的内存地址 * 4)数组越界 * 5)堆栈溢出

正文

一、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备注
0x00x34ELF头0x34表示十进制的52,刚好为ELF头的大小
0x340x2a.text 
0x600x38.data 
0x980x0.bss 
0x980x30.shstrtab 
0xc80x140Section头表一个Section头的大小为40个字节,共8个头,大小为0x140
0x2080x80.symtab 
0x2880x28.strtab 
0x2b00x10.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备注
0x00x34ELF头0x34表示十进制的52,刚好为ELF头的大小
0x340x40Program头表一个Program头的大小为32字节,共2个头,大小为0x40
0x740x2a.text 
0xa00x38.data 
0xd80x27.shstrtab 
0x1000xf0Section头表一个Section头的大小为40个字节,共6个头,大小为0xf0
0x1f00xa0.symtab 
0x2900x40.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文件详解相似的内容:

[转帖]ELF文件详解

一、ELF概述 1、ELF的定义 ELF(Executable and Linkable Format)文件是一种目标文件格式,常见的ELF格式文件包括:可执行文件、可重定位文件(.o)、共享目标文件(.so)、核心转储文件等。 ELF主要用于Linux平台,Windows下是PE/COFF格式。

【转帖】Linux开发工具 — readelf、objdump、hexdump

本博文的主要内容是:1)readelf工具查看ELF文件的信息;2)hexdump工具查看这块内存;3)objdump工具对文件进行反汇编。 前一段时间对Linux不熟,所以很多命令不知道。学习C时候需要偶尔看一下汇编用来理解。我喜欢用问题的形式来学习和总结。 1. 如何看一个程序代码变量的存储布局

[转帖]eBPF监控工具bcc系列一启航

https://blog.51cto.com/u_15333820/3453313 在eBPF篇中,我们知道虽然可用 C 来实现 BPF,但编译出来的却仍然是 ELF 文件,开发者需要手动析出真正可以注入内核的代码。工作有些麻烦,于是就有人设计了 BPF Compiler Collection(BC

[转帖]Linux内核映像vmlinux、Image、zImage、uImage区别

本文介绍几种常用的Linux内核映像的区别。 一、vmlinux vmlinux:Linux内核编译出来的原始的内核文件,elf格式,未做压缩处理。 该映像可用于定位内核问题,但不能直接引导Linux系统启动。 使用readelf命令查看RockPI 4A单板Linux内核编译的vmlinux,内容

[转帖]Linux内核映像vmlinux、Image、zImage、uImage区别

https://zhuanlan.zhihu.com/p/466226177 本文介绍几种常用的Linux内核映像的区别。 一、vmlinux vmlinux:Linux内核编译出来的原始的内核文件,elf格式,未做压缩处理。 该映像可用于定位内核问题,但不能直接引导Linux系统启动。 使用rea

[转帖]认识目标文件的格式——a.out COFF PE ELF

https://cloud.tencent.com/developer/article/1446849 1.目标文件的常用格式 目标文件是源代码编译后未进行链接的中间文件(Windows的.obj和Linux的.o),与可执行文件(Windows的.exe和Linux的ELF)的结构和内容相似,因此

[转帖]Linux命令学习手册-readelf

https://www.jianshu.com/p/405844abefae 功能 用于显示 elf 格式文件的信息。 描述 readelf 用来显示一个或者多个 elf 格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。这里的 elf-file(s) 就表示那些被检查的文件。可以支持32位

[转帖]Linux命令学习手册-readelf

https://www.jianshu.com/p/405844abefae readelf elf-file(s) 功能 用于显示 elf 格式文件的信息。 描述 readelf 用来显示一个或者多个 elf 格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。这里的 elf-file(s)

[转帖]a.out、coff、elf三种文件格式

补充:a.out早期并不是elf格式的,而是unix下另一种可执行格式,新的a.out是本文讨论了 UNIX/LINUX 平台下三种主要的可执行文件格式:a.out(assembler and link editor output 汇编器和链接编辑器的输出)、COFF(Common Object F

[转帖]

Linux ubuntu20.04 网络配置(图文教程) 因为我是刚装好的最小系统,所以很多东西都没有,在开始配置之前需要做下准备 环境准备 系统:ubuntu20.04网卡:双网卡 网卡一:供连接互联网使用网卡二:供连接内网使用(看情况,如果一张网卡足够,没必要做第二张网卡) 工具: net-to