Linux下ELF文件的格式(3)

假装没事ソ 提交于 2021-02-18 04:28:44

【说明】本文章从本人的CSDN博客搬过来的,因个人感觉CSDN的博客系统太差,so,搬到这里。

在上一篇文章中,主要介绍了几个重要的段以及这几个段中存放的信息。这里将重点介绍目标文件中的 ELF文件头 和 段表(Section Header Table)。


关于ELF文件头,它主要包含了描述整个文件的基本属性,比如ELF文件版本,目标机器型号,程序入口地址等。

关于段表,它描述了ELF文件包含的所有段的信息,比如每个段的段名,长度,在文件中的偏移,读写权限以及其他的属性。


1.再看文件头

再来看整个头文件结构

那么对于系统来讲,这个结构是怎么存储的,按什么格式存储呢?

/user/include/elf.h中,可以找到描述这个头文件的结构体,如下所示:

对比实际的内容跟这个结构体,发现从Type (e_type)开始往下是一一对应的,而前面几条信息就对应于 e_ident[16],即e_ident中存放着前几条的信息。

现在将几个重要的字段解释一下:

e_type : ELF文件类型,主要有3种,ET_REL 可重定位,ET_EXEC可执行,ET_DYN共享目标文件,一般为.so文件

e_machine : 机器类型,如EM_386,intel的x86架构,EM_68K摩托罗拉的68000系列等

e_entry : 入口地址,可执行文件被加载后,会从这个虚拟地址开始执行指令。目标文件不会执行,所以地址为0

e_shoff : 段表在文件中的偏移,即段表所在的地址,这个很重要

e_ehsize : 头文件本身的大小

e_shentsize : 段表描述符的大小,即sizeof(Elf32_Endr),一般为40

e_shnum : 段表描述符的数量,即该文件总共有多少个段

e_shstrndx : 段表字符串表所在的段在段表中的下标,很绕口,后面再讲。


2.段表

ELF文件的段结构就是由段表决定的,编译器,连接器和装载器都是靠段表来定位和访问各个段的属性的。

现在我们来仔细看看目标文件中的各个段:

这里列出来的就是段表的信息,可以看到第一个是无效的,它的类型是NULL,共有13个有效的段。其中包括上篇里我们自定义的 special段。

那么,描述段表的结构又是怎样的呢?

段表其实是一个Elf32_Shdr的数组,每一个数组元素都是一个结构体,来描述一个段。

sh_name : 段名

sh_type : 段的类型

    比如:SHT_NULL 表示无效段

                SHT_PROGBITS 表示程序段 代码段 数据段

                SHT_SYMTAB 表示该段的内容为符号表  

                 等等


sh_flags : 段的标志

    比如:SHF_WRITE 该段在进程空间中可以写

                SHF_ALLOC 表示需要在进程空间中分配信息,代码段 数据段等,注意:bss段也是这个标志,虽然是在文件中没有,但是会在进程中分配 空间

                SHF_EXECINSTR 表示可执行 像代码段


例如:.text段的类型是 SHT_PROGBITS,标志是SHF_ALLOC + SHF_EXECINSTR


sh_addr : 如果该文件被加载,表示该段在进程虚拟地址空间中的位置,否则为0

sh_offset : 段偏移,如果该段在文件中,则表示偏移,否则无意义。比如对于BSS段来讲,这个值是没有意义的

sh_size : 段的长度

sh_link sh_info : 段的链接信息


参照下表:

sh_addralign : 段地址对齐 段地址对齐于 2的sh_addralign次方,前面说过,2**2是2的2次方,即对齐4个字节,不够4的,也要占满。如果sh_addralign为1或者0,则不限制对齐。

sh_entsize :   项的长度,有些段包含了一些固定大小的项,如符号表,它的每一项占得空间都是相同的,这是该字段表示的是每个项的大小。为0则表示不含有相同大小的项。


每个段描述符的大小为sizeof(Elf32_Shdr),大小为40个字节,到文件最后结束时,大小为0x0570,即1392个字节,跟ll命令查看的大小是相同的。下面让我们看看ELF文件格式的真实存储情况:

(程序就是前面文章中用到的,环境是Ubuntu14.04 64位)


因为字节对齐的原因,中间有的段之间有一个或两个字节的空位。


3.重定位表

首先,重定位过程发生在链接时,简单的讲重定位的任务就是将编译时无法确定的地址,确定下来。

在hello.o中,有一个段,.rel.text,他的类型是SHT_REL,说明它是一个记录.text段的重定位信息的段。这个段的sh_link表示符号表的下标【为12】,sh_info表示作用于哪个段【为1,说明是 .text段】。

比如:printf函数,这个一个外部定义的函数,在编译期间是不会知道它的地址的,所以需要依靠连接器来决定它的真是运行地址(后面的文章会说到)。


4.字符串表

前面提及的几个表结构都是用固定大小的结构体来存储的,但是,字符串的大小是不固定的,不能用固定的结构来表示的。所以ELF采用了一种依赖结束符的存储结构。

从0地址取,是空串,

从1地址取,是NIHAO,

从3地址取,是HAO

这样就不用考虑字符串的长度了。

字符串表也已段的形式保存在ELF文件中,名为.strtab或.shstrtab。前者是字符串表,后者是段表字符串表。前者保存的是普通的字符串,而后者保存的则是段表用到的字符串,比如表名。

前面讲到,Elf32_Ehdr最后一个成员是e_shstrndx,他表示 段表字符串表 (.shstrtab)在 段表中的下标。在文件头里看到,最后一个成员的值是11,再查看段表11位置上的值正好是.shstrtab,即段表字符串表。


下一篇文章,将介绍ELF文件的符号,马上就会很清晰的认识这个文件格式了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!