程序员的自我修养之ELF文件格式

832 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

这一系列是《程序员的自我修养》的阅读笔记:

程序员的自我修养之程序的编译和链接

程序员的自我修养之目标文件里有什么

程序员的自我修养之ELF文件格式

前言

我们已经大致了解了ELF文件的轮廓,接着就来看看ELF文件的结构格式。

image.png

ELF目标文件格式的最前部是ELF文件头(ELF Header),紧接着是ELF文件各个段。其中ELF文件中与段有关的重要结构就是段表(Section Header Table),另外还有一些ELF中辅助的结构,比如字符串表、符号表等。

文件头

我们可以用readelf命令来详细查看ELF文件,通过-h指令来查看ELF文件头。

$readelf -h hello.o

image.png

从上面输出的结果可以看到,ELF的文件头中定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段的数量等。

ELF文件头结构及相关常数被定义在“/usr/include/elf.h”里,因为ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本。它的文件头结构也有这两种版本,分别叫做“Elf32_Ehdr”和“Elf64_Ehdr”。

“elf.h”使用typedef定义了一套自己的变量体系,如下表所示。

image.png

64位版本的elf文件头结构定义如下:

image.png

可以看到readelf输出的ELF文件头信息和ELF文件头中的结构很多都一一对应。有点例外的是“Elf64_Ehdr”中的e_ident这个成员对应了readelf输出结果中的“Magic”、“Class”、“Data”、“Version”、“OS/ABI”和“ABI Version”这6个参数,其他都一一对应。

成员readelf输出结果与含义
e_identMagic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
e_typeType: REL (Relocatable file)
ELF文件类型
e_machineMachine: Advanced Micro Devices X86-64
ELF文件的CPU平台属性
e_versionVersion: 0x1
ELF版本号
e_entryEntry point address: 0x0
入口地址。操作系统在加载完程序后从这个地址开始加载程序的指令。可重定位文件一般没有入口地址,则这个值为0。
e_shoffStart of section headers: 760 (bytes into file)
段表在文件中的偏移。值是760,表示段表是从761个字节开始。

段表

ELF文件中有很多各种各样的段,段表(SectionHeader Table)就是保存这些段的基本属性的结构。段表是ELF文件中除了文件头以外最重要的结构,它描述了ELF的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。

使用readelf工具来查看ELF文件的段,除了之前我们了解到的 .text,.data,.bss段,它还有很多其他的辅助段。

$readelf -S hello.o

image.png

重定位表

链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面。

hello.o段表中有一个.rela.text段,它的类型为 RELA,也就是说它是一个重定位表(Relocation Table)。

字符串表

ELF文件中用到了很多字符串,比如段名、变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。

一般字符串表在ELF文件中也以段的形式保存,常见的段名为“.strtab”或“.shstrtab”。这两个字符串表分别为字符串表(StringTable)和段表字符串表(Section Header String Table)。顾名思义,字符串表用来保存普通的字符串,比如符号的名字;段表字符串表用来保存段表中用到的字符串,最常见的就是段名。