ELF格式的组成结构

LF指的是Executable and Linkable Format。最初是由UNIX系统实验室作为应用程序二进制接口开发和发行的,后来逐渐发展成为了可执行文件的格式标准,在很多操作系统和非操作系统环境中都有非常广泛的应用。完整的ELF格式标准涉及了三个方面的内容。在这里我们只需要关心一个方面,那就是一个ELF格式可执行程序的组成结构。

一个ELF可执行文件格式如图8-1所示。

像图8-1那样,一个ELF可执行文件包含了一个描述全局信息的ELF文件头、若干个Program头、若干个Segment以及若干个可有可无的Section头。在Segment中保存的正是程序的运行代码,而Program头描述了各个Segment和其他必要信息,如链接库信息、文件辅助信息等。在代码真正运行时,我们往往只关心实际的代码部分,而与运行无关的其他信息则可以忽略掉。

ELF文件头

program头表

segment 1

segment 2

.............

section头表(可选)

图8-1  ELF文件结构

当操作系统需要执行一个ELF格式的文件时,只需要分析ELF文件头,然后找到Program头的位置,依次分析这些Program头,找到代表代码和数据的Segment,最后将这些Segment复制到指定的地址处,便可以运行程序了。

一个ELF文件头由如下部分组成:

代码8-4

  1. typedef unsigned int elf32_addr;
  2. typedef unsigned int elf32_word;
  3. typedef signed int elf32_sword;
  4. typedef unsigned short elf32_half;
  5. typedef unsigned int elf32_off;
  6. struct elf32_ehdr{
  7. unsigned char e_ident[16];
  8. elf32_half  e_type;
  9. elf32_half  e_machine;
  10. elf32_word  e_version;
  11. elf32_addr  e_entry;
  12. elf32_off   e_phoff;
  13. elf32_off   e_shoff;
  14. elf32_word  e_flags;
  15. elf32_half  e_ehsize;
  16. elf32_half  e_phentsize;
  17. elf32_half  e_phnum;
  18. elf32_half  e_shentsize;
  19. elf32_half  e_shnum;
  20. elf32_half  e_shstrndx;
  21. };

因为ELF可执行程序格式可以支持不同位数的处理器,所以ELF标准中自定义了一些专有的数据类型。无论是8位的处理器还是32位的处理器,这些数据类型的大小都是一致的,从而保证了文件与处理器格式的无关性。这些数据类型的大小和含义如表8-1所示。

表8-1  ELF格式中的数据规定


数据类型


大小


对齐


含义


elf32_addr


4


4


用于描述程序运行地址


elf32_word


4


4


描述无符号大整数


elf32_sword


4


4


描述有符号大整数


elf32_half


2


2


描述无符号中等大小的整数


elf32_off


4


4


描述无符号的文件偏移量

让我们结合表8-1的描述,逐个分析一下ELF文件中各部分的含义。

一个ELF文件头总是出现在文件的最开始处。其中,第一个成员是一个16个字节数组,里边记录了文件的标识、版本、编码格式等信息。

之后的两个字节是e_type成员,记录了目标文件属于ELF格式标准下的哪种类型,比如是可执行的文件还是可重定位文件,等等。根据ELF格式标准,这个值是2,代表了这个文件是一个可执行的文件,这也正是我们需要的。

接下来的两个字节e_machine描述的是该程序运行的硬件平台。在我们的例子中,这个值必须是40,表示这个应用程序是在ARM中运行的。

然后,4个字节的空间e_version用于描述应用程序的版本,通常可以是1。

接下来的4个字节就相当重要了,结构体成员e_entry记录了程序运行时的入口地址,也就是说,应用程序的第一条指令就应该出现在这个地址处。

e_phoff成员记录了第一个Program头在文件内的偏移。e_phentsize成员则代表了每一个Program头大小,再结合能够描述文件中共有多少个Program头的e_phnum成员,我们就可以遍历每一个Program头,并可以从中找到代码和数据在文件中的位置。

代码8-4中的其他成员与程序的执行关系不大,这里就不多介绍了。读者朋友们如果还对这些内容感兴趣,可以去查阅相关文档。

另外,还有一个问题需要解决。在遍历每一个Program头时,如何才能知道这个头信息所描述的Segment就是数据或代码,而不是与运行程序无关的其他信息呢?我们在Program头结构体中可以找到答案。

代码8-5

  1. struct elf32_phdr{
  2. elf32_word  p_type;
  3. elf32_off   p_offset;
  4. elf32_addr  p_vaddr;
  5. elf32_addr  p_paddr;
  6. elf32_word  p_filesz;
  7. elf32_word  p_memsz;
  8. elf32_word  p_flags;
  9. elf32_word  p_align;
  10. };

代码8-5定义了elf32_phdr结构体用于描述Program头信息。在该结构体中,与段类型直接相关的是p_type成员,只有当该成员的值为1时,才表示该Segment是要运行的代码或数据,需要在执行时加载到内存中去。

一旦确定需要进行内存加载,之后要做的就是读取p_offset成员的值,它表示要加载到内存中的这个Segment在文件中的偏移量,再结合描述Segment大小的成员p_filesz,就可以精确地定位程序的代码和数据了。

最后,程序还要读取p_vaddr成员,它表示这个Segment应该出现在内存的哪个位置。这样就可以将该Segment从文件中复制到正确的内存地址处了。

ELF格式的组成结构

时间: 2024-08-25 10:34:29

ELF格式的组成结构的相关文章

ELF格式解析库之抽象数据类型

抽象?抽谁的象? ELF是一种链接执行格式,它规定了对于一个ELF文件的基本数据类型是什么样的.可是,要解析一个ELF文件,而这个ELF文件或者是32Bits 或者是 64Bits,反正字长是未定的,怎么办?难道我们要定义两套解析的接口,以对应不同的字长的ELF文件吗?如果要这样做,不是不可以,只是那样做为接口的设计增加了太大的负担.这里我们采用"抽象"的方式,将已有的两套基础数据结构封装成一个兼容的数据结构.这样,我们设计解析接口时,可以做到尽量的简化,大大的减轻了工作量. 因此,这

ELF格式解析库之基本数据类型

ELF格式简介 ELF是现代linux/unix流行的程序可执行链接格式,它有官方定义文档,具体的资料在Tool Interface Standard Executable and Linking Format Specification version 1.2 有其对应的详细描述.我在这里不会讲述关于这个格式详细描述,如果我叙述的有什么不对的地方,欢迎指正出来.当然一切都以官方的参考文档为标准,建议您在阅读本文的时候,手边最好有一份官方定义文档参考. ELF由三个头表描述:ELF header

elf格式分析

近期研究了一下elf文件格式,发现好多资料写的都比較繁琐,可能会严重打击学习者的热情,我把自己研究的结果和大家分享,希望我的描写叙述可以简洁一些. 一.基础知识 elf是一种文件格式,用于存储Linux程序. 它内部都有一些什么信息呢?大概包含编制好的计算机指令,数据,计算机在须要的时候把这个文件读取到内存中,cpu就能够从内存中一条一条的读取指令来运行了. 所以说想明确elf格式,我们应该了解一下计算机运行程序须要那些信息.所以这一节,我们补充一些计算机系统的基础知识. 进程和虚拟内存: Li

linux下的ELF格式分析

ELF格式文档详解 一,ELF格式综述 ELF(Executable and Linkable Format)是Linux下的一种格式标准,Linux中的ELF格式文件一共有四种: ●可重定位文件(Relocatable File):这类文件包含了代码和数据,可被用来链接成可执行文件或者共享目录文件,扩展名为.o ●可执行文件(Executable File):这类文件包含了可以直接执行的程序,一般没有扩展名 ●共享目录文件(Shared Object File):这类文件包含了代码和数据,扩展

ELF格式的重定位原理分析

前面有篇文章分析了ELF格式,也只是让我们对目标文件有了一个大概的了解,并没有说明一个十分重要的问题:重定位,今天重新看了下重定位的资料,终于弄懂了重定位的过程,下面来做一个分析. 我们将使用下面两个源代码中的文件a.c和b.c展开分析: //a.c extern int shared; int main() { int a=100; swap(&a,&shared); } //b.c int shared=1; void swap(int *a,int *b) { *a^=*b^=*a^

检查打开的文件是不是ELF格式的文件,提取“特别”的节中的信息

//检查打开的文件是不是ELF格式的文件 //检验ELF头 //获得节头表的地址 //遍历节头表,依次查看一个节的属性,从节头字符串表中找到其名称,如果名称和"特别"的section名匹配,则找到此节的地址 //提取"特别"的节中的信息 //显示上面提取的信息 #include<stdio.h> #include<elf.h> #include<stdlib.h> #include<string.h> int main

ELF格式解析库之提取信息

看,宝藏就在那儿 在上一篇文章中,我们提到用按图索骥比喻库的初始化过程,那么现在有了地图,接下来的事情就是去寻找我们感兴趣的宝藏了.这个宝藏可能是一个ELF文件的程序文本段,也有可能是程序的某个不知名的代码段,这些都取决于你想要什么信息.我建议你去阅读ELF 的官方标准,那里边讲的比较清楚. 我这里只是实现了几个提取诸如:程序的大小端,能执行的CPU位数,程序的入口点,以及获得程序的所有节的编号和根据节的编号获取该节的详细信息. 提取信息:程序的大小端 1: long ELF_GetELFEnd

.elf格式内容

arm-linux-ld 可以将程序链接成我们arm平台下的可运行的程序 以之前使用过的led程序为例: 首先: 1 arm-linux-gcc -g -c led.S (-g是表示产生调试信息, -c是表示只编译不链接) 产生led.o文件 1 arm-linux-ld -Tled.lds -o led.elf led.o 产生.elf格式的文件 然后通过: arm-linux-readelf命令:读出.elf的详细内容: 1 arm-linux-readelf -a led.elf(显示所有

ELF格式解析库之初始化

寻宝要先设计路线图 对一个ELF文件来说,它里边装个一个程序的所有信息.我们想要拿的程序信息就在这个ELF文件的某个地址处.可是我们根本就不知道这个具体的地址是哪个,怎么办?举个较形象的例子来描述我们将要的做的事情.峨眉山的山道上放着一大箱金子,我们想搞到这箱黄金.所以在动手之前,我们应该先搞清楚这箱黄金具体位置在哪条山路上,然后设计出几条可行的路线图,接着才是沿着先前设计好的路线去找宝藏.只不过,在这里我们要找的黄金变成了程序某个部分的信息,而那座峨眉山则变成了一个ELF文件. 所以,寻宝要先