现代x86-64Linux和Unix系统使用可执行可链接格式(Execut- ableand LinkableFormat, ELF),与ELF同类型的文件是windows上的PE文件和MacOS-X上的Mach-O文件
本篇文章讲述ELF文件的文件头和段表
BSS全称是block started by symbol。
BSS是ELF文件中的一个段,该段存储全局未初始化的变量和局部未初始化的静态变量。
之所以单独分出一个bss段,是为了节省ELF文件的大小
。BSS段在ELF文件不占用空间
程序运行的时候bss是占用内存的。并且ELF文件需要记录bss占用空间的大小。
总的来说,ELF文件包含三种类型:
与其他可重定位目标文件一起链接成一个可执行文件
动态加载到内存,并且与可执行文件进行链接
这是在linux系统上通过readelf -h
命令获取到的elf文件的头信息:
ELF Header:
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
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 400 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 13
Section header string table index: 10
EFL文件最开始是一个16字节大小的序列,这16个字节的解释如下:
type表示这个ELF文件的类型,占用两个字节
machine表示当前文件可使用的机器平台,占用两个字节
Version: 0x1
ELF版本号。一般为常数1
这个表示程序执行开始的入口地址,操作系统在加载完该程序后从这个地址开始执行进程的指令
对于REL来说是0,对于EXEC和DYN才有意义
程序头
的偏移,因为可重定向文件会在链接的时候根据可执行文件或者共享文件自动重定向地址,所以可重定向文件不需要这个字段,除此以外,可执行文件和共享文件都需要这个字段
段表在文件中的偏移
ELF标志位,用来标识一些ELF文件平台相关的属性 。相关常量的格式一般为EF_machinc_flag
,machine为平台,flag为标志
当前ELF头的大小
程序头
的大小
程序头表的数目
段表描述符的大小
段表描述符的数目
段表字符串表所在的段在段表中的下标
我们知道ELF文件中有很多各种各样的段,这个段表(SectionHeader Table)就是保存这些段的基本属性的结构。段表是ELF文件中除了文件头以外最重要的结构,它描述了ELF的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。也就是说,ELF 文件的段结构就是由段表决定的,编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。
在linux系统下可以使用
readelf -S 程序名.o
来获取ELF文件的所有段,下面是我们获取的一个例子
There are 13 section headers, starting at offset 0x190:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
0000000000000054 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000006b8
0000000000000078 0000000000000018 11 1 8
[ 3] .data PROGBITS 0000000000000000 00000094
0000000000000008 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 0000009c
0000000000000004 0000000000000000 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 0000009c
0000000000000004 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000a0
000000000000002c 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 000000cc
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 000000d0
0000000000000058 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 00000730
0000000000000030 0000000000000018 11 8 8
[10] .shstrtab STRTAB 0000000000000000 00000128
0000000000000061 0000000000000000 0 0 1
[11] .symtab SYMTAB 0000000000000000 000004d0
0000000000000180 0000000000000018 12 11 8
[12] .strtab STRTAB 0000000000000000 00000650
0000000000000065 0000000000000000 0 0 1
段表的每一项都是一个结构体的结构,也就是说段表其实是一个结构体的数组,上面列出的段表的数据确实有13个表项,但是第一项是NULL,接下来,我们分析一下每个表项的数据结构
段名:段名是一个字符串,他位于一个.shstrtab表中,该项值表示段名在.shstrtab表中的偏移
段类型:段类型决定了段是干什么用的,段类型的列表如下:
段标志位:段的标志位标志段在虚拟地址空间中的属性,比如可写,是否可执行等,标志位是可以进行组合操作的。
段虚拟地址:如果段可以被加载,则该值为段被加载到内存空间的虚拟地址,否则该值为0
段偏移地址:如果段存在与文件中,该值表示段在文件中的偏移地址,否则该值没意义.比如对于bss段,该值就没有意义
段的长度
段的链接
段的信息
段对齐地址:有些段对段地址对齐有要求,比如我们假设有个段刚开始的位置包含了一个double变量,因为Intel x86系统要求浮点数的存储地址必须是本身的整数倍,也就是说保存double变量的地址必须是8字节的整数倍。这样对一个段来 说,它的sh_addr必须是8的整数倍。
由于地址对齐的数量都是2的指数倍,该字段表示是地址对齐数量中的指数,即字段值为3表示对齐为2的3次方倍,即8倍.
如果该字段值为1或者0,表示没有对齐要求
项的长度:有些段包含了一些固定大小的项,比如符号表,它包含的每个符号所占的大小都是一样的。对于这种段,该值表示每个项的大小。如果为0,则表示 该段不包含固定大小的项
我们根据获取到的段的信息分析一下类型和标志位的信息
数据段的类型是SHT_PROGBITS
数据段的标志位是WA,就是SHT_WRITE|SHT_ALLOC,说明数据段需要分配内存,并且是可写的
。
代码段的类型是SHT_PROGBITS
代码段的标志位是AX,就是SHT_ALLOC | SHT_EXECINSTR,说明代码段需要分配内存,并且是只读的,可以被执行
。
bss段的类型是SHT_NOBITS,表示在文件中没有内容
bss段的标志位是WA,SHT_WRITE|SHT_ALLOC,说明bss段需要分配内存,并且是可写的
。
rodata段的类型是SHT_PROGBITS
rodata段的标志位是A,就是SHT_ALLOC,说明rodata段需要分配内存,并且是只读的
。rodata其实就是read only data。
段的类型是SHT_RELA,说明该段包含了重定位信息
该段没有标志位,说明不需要分配空间。链接的时候使用
段的类型是SHT_PROGBITS
该段没有标志位,说明不需要分配空间。
段的类型是SHT_STRTAB
我们之前说过,该表包含了段表中字符串的内容
段的类型是SHT_STRTAB
如果该ELF文件中有可装载的段须要用到该字符串表,那么该字符串表也将被装 载到进程空间,则有SHF_ ALLOC 标志位
段的类型是SHT_SYMTAB,是符号表
如果一个段的类型和链接相关,不管是静态链接还是动态链接,那么段中的section link和section information就是有意义的了。
哪些类型是与链接相关的呢?我们根据段类型就行查找,下面这些应该是与链接相关的:
我们列一下不同的类型section link和section information表示的意义
section link:操作系统相关
section information:操作系统相关
section link:该段使用的符号表在段表的下标
section information:该重定位表所用的段在段表的下标
section link:该段使用的字符串表在段表的下标
section information:0
section link:该段使用的符号表在段表的下标
section information:0