持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
这一系列是《程序员的自我修养》的阅读笔记:
程序员的自我修养之ELF文件格式
前言
我们已经大致了解了ELF文件的轮廓,接着就来看看ELF文件的结构格式。
ELF目标文件格式的最前部是ELF文件头(ELF Header),紧接着是ELF文件各个段。其中ELF文件中与段有关的重要结构就是段表(Section Header Table),另外还有一些ELF中辅助的结构,比如字符串表、符号表等。
文件头
我们可以用readelf命令来详细查看ELF文件,通过-h指令来查看ELF文件头。
$readelf -h hello.o
从上面输出的结果可以看到,ELF的文件头中定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段的数量等。
ELF文件头结构及相关常数被定义在“/usr/include/elf.h”里,因为ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本。它的文件头结构也有这两种版本,分别叫做“Elf32_Ehdr”和“Elf64_Ehdr”。
“elf.h”使用typedef定义了一套自己的变量体系,如下表所示。
64位版本的elf文件头结构定义如下:
可以看到readelf输出的ELF文件头信息和ELF文件头中的结构很多都一一对应。有点例外的是“Elf64_Ehdr”中的e_ident这个成员对应了readelf输出结果中的“Magic”、“Class”、“Data”、“Version”、“OS/ABI”和“ABI Version”这6个参数,其他都一一对应。
| 成员 | readelf输出结果与含义 |
|---|---|
| e_ident | Magic: 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_type | Type: REL (Relocatable file) ELF文件类型 |
| e_machine | Machine: Advanced Micro Devices X86-64 ELF文件的CPU平台属性 |
| e_version | Version: 0x1 ELF版本号 |
| e_entry | Entry point address: 0x0 入口地址。操作系统在加载完程序后从这个地址开始加载程序的指令。可重定位文件一般没有入口地址,则这个值为0。 |
| e_shoff | Start of section headers: 760 (bytes into file) 段表在文件中的偏移。值是760,表示段表是从761个字节开始。 |
段表
ELF文件中有很多各种各样的段,段表(SectionHeader Table)就是保存这些段的基本属性的结构。段表是ELF文件中除了文件头以外最重要的结构,它描述了ELF的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。
使用readelf工具来查看ELF文件的段,除了之前我们了解到的 .text,.data,.bss段,它还有很多其他的辅助段。
$readelf -S hello.o
重定位表
链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面。
hello.o段表中有一个.rela.text段,它的类型为 RELA,也就是说它是一个重定位表(Relocation Table)。
字符串表
ELF文件中用到了很多字符串,比如段名、变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。
一般字符串表在ELF文件中也以段的形式保存,常见的段名为“.strtab”或“.shstrtab”。这两个字符串表分别为字符串表(StringTable)和段表字符串表(Section Header String Table)。顾名思义,字符串表用来保存普通的字符串,比如符号的名字;段表字符串表用来保存段表中用到的字符串,最常见的就是段名。