读书笔记--《程序员的自我修养》第3章:目标文件里有什么(2)

3.4 ELF文件结构描述

ELF文件结构如图所示:
这里写图片描述
ELF目标文件格式最前面是ELF文件头,它包含了描述整个文件的基本属性,如ELF文件版本、目标机器型号、程序入口地址等。接着是ELF文件各个段。
其中ELF文件中与段有关的重要结构是段表。该表描述了ELF文件包含的所有段的信息,如每个段的段名、段长、在文件中的偏移、读写权限及段的其他属性。

3.4.1 文件头
输入命令:$readelf -h SimpleSection.o
可以看出ELF文件头中定义了ELF魔数、文件机器字节长度、数据存储方式、版本等信息。
ELF文件头结构及相关常数被定义在”/usr/include/elf.h”里,因为ELF文件在各个平台下都通用,因此有32位版本和64位版本。
我们以32位文件头结构Elf32_Ehdr为例来描述,它的定义如下:
typedef struct {
unsigned char e_ident[16];
Elf32_Half e_type; //ELF文件类型
Elf32_Half e_machine; //ELF文件的CPU平台属性,相关常量以EM_开头
Elf32_Word e_version; //ELF版本号,一般为1
Elf32_Addr e_entry; //入口地址,规定ELF程序的入口虚拟地址。OS加载完程序后从这里开始执行进 程的指令
Elf32_Off e_phoff;
Elf32_Off e_shoff; //段表在文件中的偏移
Elf32_Word e_flags; //ELF标志位,用来标识一些ELF文件平台相关的属性。
Elf32_Half e_ehsize; //ELF文件头本身的大小
Elf32_Half e_phentsize;
Elf32_Half e_ehnum;
Elf32_Half e_shentsize; //段表描述符的大小,一般等于sizeof(Elf32_Shdr)
Elf32_Half e_shnum; //段表描述符的数量。等于ELF文件中拥有的段的数量。这里为12
Elf32_Half e_shstrndx; //段表字符串表所在的段的段表中的下标。
}Elf32_Ehdr;
发现ELF文件头结构跟前面的readelf输出的ELF文件头信息相比,很多都是一一对应的。其中Elf32_eiden整个成员对应readelf输出结果中的Class Data Version OS/ABI和ABIversion。

1、ELF魔数
最开始的4个字节是所有ELF文件都必须相同的标识码,分别是0x7f、0x45、0x4c、0x46,第一个字节对应ASCII字符里面的DEL控制符,后3个字节刚好是ELF这3个字母的ASCII。这4个字节又被称为ELF文件的魔数,比如a.out格式最开始两个字节为0x01、0x07,PE最开始的两个字节是0x4d、0x5a,即ASCII字符的MZ。这种魔数用来确认文件的类型,OS在加载可执行文件时会确认魔数是否正确,如果不正确会拒绝加载。
接下来的一个字节是用来标识ELF的文件类的,0x01表示32位的,0x02表示64位的;第6个字是字节序,规定该ELF文件是大端的还是小端的;第7个字节规定ELF文件的主版本号,一般是1;后面的9个字节ELF标准没有定义,一般写0,有些平台会使用这9个字节作为扩展标志。

2、文件类型
e_type成员表示ELF文件类型,即前面提到的3中ELF文件类型。系统通过这个常量判断ELF文件的类型,而不是通过扩展名。
这里写图片描述

3、机器类型
e_machine成员表示该ELF文件的平台属性。比如3表示ELF文件只能在Intel x86机器下使用。
这里写图片描述

3.4.2 段表
段表用来保存这些段的基本属性的结构,描述各个段的信息。编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。段表在ELF文件中的位置由ELF文件头中的e_shoff成员决定。
输入以下命令:$readelf -S SimpleSection.o
readelf输出的结果就是ELF文件段表的内容。段表是一个以“Elf32_Shdr”结构体为元素的数组。数组中元素的个数等于段的个数,每个“Elf32_Shdr”结构体对应一个段。“Elf32_Shdr”又称为段描述符。它被定义在“/usr/include/elf.h”
Elf32_Shdr段描述符的结构如下:
这里写图片描述
各个成员表示如下:
这里写图片描述
对照结构体和readelf -S的结果,可以看到该成员对应输出结果中从第二列“Name”开始的每一列。
1、段的类型
段的名字只有在链接和编译过程中才有意义,它不能真正表示段的类型。我们也可以将一个数据段命名为“.text”。对于编译器和链接器来说,主要决定段的属性的是段的类型(sh_type)和段的标志位(sh_flags)。段的类型相关常量以SHT_开头。
这里写图片描述
这里写图片描述
2、段的标志位
表示该段在进程虚拟地址空间中的属性。比如是否可写,是否可执行等。相关常量以SHF_开头。
这里写图片描述
3、段的链接信息
如果段的类型是与链接相关的,比如重定位表、符号表等,那么sh_link和sh_info这两个成员所包含的意义如表所示。
这里写图片描述

3.4.3 重定位表
链接器在处理目标文件时,需要对目标文件中某些部位进行重定位。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个需要重定位的代码段或数据段,都会有一个相应的重定位表。
一个重定位表同时也是ELF的一个段,那么这个段的类型(sh_type)就是“SHT_REL”类型的,它的“sh_link”表示符号表的下标,它的“sh_info”表示它作用于哪个段。比如“.rel.text”作用于“.text”段,而“.text”段的下标为1,那么“.rel.text”的“sh_info”为1。

3.4.4 字符串表
ELF文件中用到很多字符串,由于长度不同,用固定的结构比较困难。常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。
如字符串表为:
这里写图片描述
那么偏移与它们对应的字符串如表所示:
这里写图片描述
一般字符串表在ELF文件中也以段的形式保存,常见的段名为”.shstrtab“和”.strtab“。这两个字符串分别表示段表字符串表和字符串表。字符串表用来保存普通的字符串,段表字符串表用来保存段表中用到的字符串,最常见的就是段名(sh_name)。
ELF文件头中的”e_shstrtab“表示段表字符串表在段表中的下标。本例中是10
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_15727809/article/details/82629465