ELF文件头和段表

前言

现代x86-64Linux和Unix系统使用可执行可链接格式(Execut- ableand LinkableFormat, ELF),与ELF同类型的文件是windows上的PE文件和MacOS-X上的Mach-O文件

本篇文章讲述ELF文件的文件头和段表

BSS

BSS全称是block started by symbol。
BSS是ELF文件中的一个段,该段存储全局未初始化的变量和局部未初始化的静态变量。
之所以单独分出一个bss段,是为了节省ELF文件的大小。BSS段在ELF文件不占用空间

程序运行的时候bss是占用内存的。并且ELF文件需要记录bss占用空间的大小。

为什么代码段数据段分离

  • 程序启动后,代码段和数据段分离,一般来说,代码段是只读的,数据段是可读可写的。所以这两个数据段可以分别设置读写权限,这样可以防止代码被篡改。
  • 对于现代计算机,都有强大的缓存体系。由于缓存的代码或者数据访问速度远远高于内存的访问,所以提高缓存命中率至关重要,现代计算机通常指林缓存和数据缓存是分开的。代码段和数据段的分离有利于提高缓存的命中率。提高程序运行速度。
  • 还有最重要的一点,就是共享。如果有多个同一个程序的进程同时运行。程序的代码是可以共享的。程序的只读数据也是可以共享的。这可以节省很大的内存空间。这也是为什么数据段外还会有只读数据段

总的来说,ELF文件包含三种类型:

  • 可重定位目标文件:包含二进制代码和数据,可以与其他可重定位目标文件一起链接成一个可执行文件
  • 可执行文件:操作系统可以使用加载器运行的一种文件格式,windows下的exe文件就是可执行文件
  • 共享文件:这种文件可以在程序运行时动态加载到内存,并且与可执行文件进行链接

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

magic

EFL文件最开始是一个16字节大小的序列,这16个字节的解释如下:

  • 魔数字节:用于确定文件类型的标识。比如.out文件前两个字节为0x01和0x07,PE/COFF文件的前两字节为0x4d和0x5a,代表字符MZ。当前ELF文件的前四个字节为魔数字节。第一个字节为DEL控制符,接下来三个字节为ELF的ascii表示。
  • 文件类:接下来的一个字节表示文件类,如果是0x01表示32位,如果是0x02表示64位,如果是0x00表示无效文件。当前文件为0x02,表示64位
  • 字节序:接下来的一个字节表示文件字节序是大端还是小端,如果是0x01表示小端格式,如果是0x02表示大端格式,如果是0x00表示无效格式。当前文件为0x01,表示小端格式
  • ELF版本:接下来一个字节表示ELF文件版本。一般是1,因为ELF标准自1.2版以后就再也没有更新了
  • 剩余字节:后面的9个字节ELF标准没有定义,一般为0。有些平台会使用这9个字节作为扩展标志。

type

type表示这个ELF文件的类型,占用两个字节

  • REL:表示可重定向文件,字节码为1
  • EXEC:表示可执行文件,字节码为2
  • DYN:共享文件,字节码为3

machine

machine表示当前文件可使用的机器平台,占用两个字节

  • No machine: 字节码为0
  • AT&T WE 32100: 字节码为1
  • SUN SPARC: 字节码为2
  • Intel 80386: 字节码为3
  • Motorola m68k family: 字节码为4
  • Motorola m88k family: 字节码为5
  • Intel 80860: 字节码为7
  • MIPS R3000 big-endian: 字节码为8
  • IBM System/370: 字节码为9
  • MIPS R3000 little-endian: 字节码为10
  • HPPA: 字节码为15
  • Fujitsu VPP500 : 字节码为17
  • Sun’s “v8plus”: 字节码为18
  • Intel 80960: 字节码为19
  • PowerPC: 字节码为20
  • PowerPC 64-bit: 字节码为21
  • IBM S390: 字节码为22
  • NEC V800 series: 字节码为36
  • Fujitsu FR20: 字节码为36
  • TRW RH-32: 字节码为38
  • Motorola RCE: 字节码为39
  • ARM: 字节码为40
  • Digital Alpha: 字节码为41
  • Hitachi SH: 字节码为42
  • SPARC v9 64-bit: 字节码为43
  • Siemens Tricore: 字节码为44
  • Argonaut RISC Core: 字节码为45
  • Hitachi H8/300: 字节码为46
  • Hitachi H8/300H: 字节码为47
  • Hitachi H8S: 字节码为48
  • Hitachi H8/500: 字节码为49
  • Intel Merced: 字节码为50
  • Stanford MIPS-X : 字节码为51
  • Motorola Coldfire: 字节码为52
  • Motorola M68HC12: 字节码为53
  • Fujitsu MMA Multimedia Accelerator: 字节码为54
  • Siemens PCP: 字节码为55
  • Sony nCPU embeeded RISC: 字节码为56
  • Denso NDR1 microprocessor: 字节码为57
  • Motorola Start*Core processor: 字节码为58
  • Toyota ME16 processor: 字节码为59
  • STMicroelectronic ST100 processor: 字节码为60
  • Advanced Logic Corp. Tinyj emb.fam: 字节码为61
  • AMD x86-64 architecture: 字节码为62
  • Sony DSP Processor: 字节码为63
  • Siemens FX66 microcontroller : 字节码为66
  • STMicroelectronics ST9+ 8/16 mc: 字节码为67
  • STmicroelectronics ST7 8 bit mc: 字节码为68
  • Motorola MC68HC16 microcontroller: 字节码为69
  • Motorola MC68HC11 microcontroller : 字节码为70
  • Motorola MC68HC08 microcontroller: 字节码为71
  • Motorola MC68HC05 microcontroller: 字节码为72
  • Silicon Graphics SVx: 字节码为73
  • STMicroelectronics ST19 8 bit mc: 字节码为74
  • Digital VAX: 字节码为75
  • Axis Communications 32-bit embedded processor: 字节码为76
  • Infineon Technologies 32-bit embedded processor: 字节码为77
  • Element 14 64-bit DSP Processor: 字节码为78
  • LSI Logic 16-bit DSP Processor: 字节码为79
  • Donald Knuth’s educational 64-bit processor : 字节码为80
  • Harvard University machine-independent object files: 字节码为81
  • SiTera Prism: 字节码为82
  • Atmel AVR 8-bit microcontroller : 字节码为83
  • Fujitsu FR30: 字节码为84
  • Mitsubishi D10V: 字节码为85
  • Mitsubishi D30V: 字节码为86
  • NEC v850: 字节码为87
  • Mitsubishi M32R: 字节码为88
  • Matsushita MN10300 : 字节码为89
  • Matsushita MN10200: 字节码为90
  • picoJava: 字节码为91
  • OpenRISC 32-bit embedded processor: 字节码为92
  • ARC Cores Tangent-A5: 字节码为93
  • Tensilica Xtensa Architecture: 字节码为94
  • ARM AARCH64: 字节码为183
  • Tilera TILEPro: 字节码为188
  • Xilinx MicroBlaze: 字节码为189
  • Tilera TILE-Gx: 字节码为191

Version

Version: 0x1
ELF版本号。一般为常数1

Entry point address

这个表示程序执行开始的入口地址,操作系统在加载完该程序后从这个地址开始执行进程的指令
对于REL来说是0,对于EXEC和DYN才有意义

Start of program headers

程序头的偏移,因为可重定向文件会在链接的时候根据可执行文件或者共享文件自动重定向地址,所以可重定向文件不需要这个字段,除此以外,可执行文件和共享文件都需要这个字段

Start of section headers

段表在文件中的偏移

Flags

ELF标志位,用来标识一些ELF文件平台相关的属性 。相关常量的格式一般为EF_machinc_flag,machine为平台,flag为标志

Size of this header

当前ELF头的大小

Size of program headers

程序头的大小

Number of program headers

程序头表的数目

Size of section headers

段表描述符的大小

Number of section headers

段表描述符的数目

Section header string table index

段表字符串表所在的段在段表中的下标

段表

我们知道ELF文件中有很多各种各样的段,这个段表(SectionHeader Table)就是保存这些段的基本属性的结构。段表是ELF文件中除了文件头以外最重要的结构,它描述了ELF的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。也就是说,ELF 文件的段结构就是由段表决定的,编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。

  • 段表在ELF文件中的位置由ELF文件头的Start of section headers成员决定,当前为400
  • 段表的数量由ELF文件头的Number of section headers决定,当前为13

在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,接下来,我们分析一下每个表项的数据结构

section name

段名:段名是一个字符串,他位于一个.shstrtab表中,该项值表示段名在.shstrtab表中的偏移

section type

段类型:段类型决定了段是干什么用的,段类型的列表如下:

  • SHT_NULL:无效段,字节码为0
  • SHT_PROGBITS:程序段、代码段、数据段,字节码为1
  • SHT_SYMTAB:段的内容为符号表,字节码为2
  • SHT_STRTAB:段的内容为字符串表,字节码为3
  • SHT_RELA:段包含重定位信息,字节码为4
  • SHT_HASH:段包含符号表的哈希表,字节码为5
  • SHT_DYNAMIC:段包含动态链接信息,字节码为6
  • SHT_NOTE:段包含提示性信息,字节码为7
  • SHT_NOBITS:段在文件中没有内容,比如bss段,字节码为8
  • SHT_REL:段包含重定位信息,字节码为9
  • SHT_SHLIB:保留类型,字节码为10
  • SHT_DNYSYM:段包含动态链接的符号表,字节码为11

section flag

段标志位:段的标志位标志段在虚拟地址空间中的属性,比如可写,是否可执行等,标志位是可以进行组合操作的。

  • SHT_WRITE:该段在进程中可写,字节码为1
  • SHT_ALLOC:该段在进程中需要分配空间,有些包含指示或者控制信息的段不需要在进程空间中分配空间。他们一般不会有这个标志,像代码段、数据段、bss段都会有这个标志,字节码为2
  • SHT_EXECINSTR:表示该段在进程中可以执行,一般指代码段,字节码为4

section address

段虚拟地址:如果段可以被加载,则该值为段被加载到内存空间的虚拟地址,否则该值为0

section offset

段偏移地址:如果段存在与文件中,该值表示段在文件中的偏移地址,否则该值没意义.比如对于bss段,该值就没有意义

section size

段的长度

section link

段的链接

section information

段的信息

section address alignment

段对齐地址:有些段对段地址对齐有要求,比如我们假设有个段刚开始的位置包含了一个double变量,因为Intel x86系统要求浮点数的存储地址必须是本身的整数倍,也就是说保存double变量的地址必须是8字节的整数倍。这样对一个段来 说,它的sh_addr必须是8的整数倍。
由于地址对齐的数量都是2的指数倍,该字段表示是地址对齐数量中的指数,即字段值为3表示对齐为2的3次方倍,即8倍.
如果该字段值为1或者0,表示没有对齐要求

section entry size

项的长度:有些段包含了一些固定大小的项,比如符号表,它包含的每个符号所占的大小都是一样的。对于这种段,该值表示每个项的大小。如果为0,则表示 该段不包含固定大小的项

常用段的类型和标志

我们根据获取到的段的信息分析一下类型和标志位的信息

.data

数据段的类型是SHT_PROGBITS
数据段的标志位是WA,就是SHT_WRITE|SHT_ALLOC,说明数据段需要分配内存,并且是可写的

.text

代码段的类型是SHT_PROGBITS
代码段的标志位是AX,就是SHT_ALLOC | SHT_EXECINSTR,说明代码段需要分配内存,并且是只读的,可以被执行

.bss

bss段的类型是SHT_NOBITS,表示在文件中没有内容
bss段的标志位是WA,SHT_WRITE|SHT_ALLOC,说明bss段需要分配内存,并且是可写的

.rodata

rodata段的类型是SHT_PROGBITS
rodata段的标志位是A,就是SHT_ALLOC,说明rodata段需要分配内存,并且是只读的。rodata其实就是read only data。

.rela.text

段的类型是SHT_RELA,说明该段包含了重定位信息
该段没有标志位,说明不需要分配空间。链接的时候使用

.comment

段的类型是SHT_PROGBITS
该段没有标志位,说明不需要分配空间。

.shstrtab

段的类型是SHT_STRTAB
我们之前说过,该表包含了段表中字符串的内容

.strtab

段的类型是SHT_STRTAB
如果该ELF文件中有可装载的段须要用到该字符串表,那么该字符串表也将被装 载到进程空间,则有SHF_ ALLOC 标志位

.symtab

段的类型是SHT_SYMTAB,是符号表

段的链接和信息

如果一个段的类型和链接相关,不管是静态链接还是动态链接,那么段中的section link和section information就是有意义的了。
哪些类型是与链接相关的呢?我们根据段类型就行查找,下面这些应该是与链接相关的:

  • SHT_SYMTAB:段的内容为符号表,字节码为2
  • SHT_RELA:段包含重定位信息,字节码为4
  • SHT_HASH:段包含符号表的哈希表,字节码为5
  • SHT_DYNAMIC:段包含动态链接信息,字节码为6
  • SHT_REL:段包含重定位信息,字节码为9
  • SHT_DNYSYM:段包含动态链接的符号表,字节码为11

我们列一下不同的类型section link和section information表示的意义

SHT_SYMTAB、SHT_DNYSYM

section link:操作系统相关
section information:操作系统相关

SHT_RELA、SHT_REL*

section link:该段使用的符号表在段表的下标
section information:该重定位表所用的段在段表的下标

SHT_DYNAMIC

section link:该段使用的字符串表在段表的下标
section information:0

SHT_HASH

section link:该段使用的符号表在段表的下标
section information:0

你可能感兴趣的:(程序设计-编译器,编辑器)