GCC编译器原理(二)------编译原理一:ELF文件(2)

四、 ELF 文件格式分析

  • ELF文件(目标文件)格式主要四种:
    • 可重定向文件:
      • 文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。(目标文件或者静态库文件,即linux通常后缀为 .a 和 .o 的文件)
    • 可执行文件:
      • 文件保存着一个用来执行的程序。(例如bash,gcc等)
    • 共享目标文件:
      • 共享库。文件保存着代码和合适的数据,用来被下连接编辑器和动态链接器链接。(linux下后缀为 .so 的文件。)
      • 执行文件的格式与上述两种文件的格式之间的区别主要在于观察的角度不同:一种称为连接视图(Linking View),一种称为执行视图(Execution View)
    • 核心转储文件(core dump):
      • 当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件

目标文件既要参与程序链接又要参与程序执行。 出于方便性和效率考虑, 目标文件格式提供了两种并行视图,分别反映了这些活动的不同需求。

GCC编译器原理(二)------编译原理一:ELF文件(2)_第1张图片

  • ELF header:ELF 文件头
    • 包含了描述整个文件的基本属性,比如 ELF 文件版本、目标机器型号、程序入口地址等等
  • Program header table:程序表头。如果存在的话,告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表
  • Section or Segment:节或段
    • ELF 文件中包含的各个节,具体见后面章节。
  • Section header table:节头表,可选
    • 每个节区在表中都有一项,描述了 ELF 文件包含的所有节的信息,比如每个节的节名、节的长度、在文件中的偏移、读写权限及段的其他属性
    • 用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
  • String tables:字符串表
  • Symbol tables:符号表

注意:尽管图中显示的各个组成部分是有顺序的,实际上除了 ELF 头部表以外,其他节区和段都没有规定的顺序。

4.1 ELF header:文件头

文件的最开始几个字节给出如何解释文件的提示信息。 这些信息独立于处理器, 也独立于文件中的其余内容。

还是对 hello.c 这段代码进行分析,对编译出来的 a.out 文件执行命令:readelf -h a.out

GCC编译器原理(二)------编译原理一:ELF文件(2)_第2张图片

  • ELF 文件头中的定义如下:
    • Magic:ELF 魔数
    • Class:文件机器字节长度
    • Data:数据存储方式
    • Version:版本
    • OS/ABI:运行平台
    • ABI Version:ABI版本
    • Type:ELF 文件类型
    • Machine:硬件平台
    • Version:硬件平台版本
    • Entry point address:入口地址
    • Start of program headers:程序头入口和长度
    • Start of section headers:节头的位置和长度
    • Flags:处理器标志
    • 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:段表头字符串表的位置

段由若干个节(Section)构成,节头表对每一个节的信息有相关描述。对可执行程序而言,节头表是可选的。 ELF 头部是一个关于本文件的路线图(road map),从总体上描述文件的结构。下面是ELF头部的数据结构:

  • /usr/include/elf.h
 1 typedef struct
 2 {
 3     unsigned char e_ident[EI_NIDENT];     /* 魔数和相关信息 */
 4     Elf32_Half    e_type;                 /* 目标文件类型 */
 5     Elf32_Half    e_machine;              /* 硬件体系 */
 6     Elf32_Word    e_version;              /* 目标文件版本 */
 7     Elf32_Addr    e_entry;                /* 程序进入点 */
 8     Elf32_Off     e_phoff;                /* 程序头部偏移量 */
 9     Elf32_Off     e_shoff;                /* 节头部偏移量 */
10     Elf32_Word    e_flags;                /* 处理器特定标志 */
11     Elf32_Half    e_ehsize;               /* ELF头部长度 */
12     Elf32_Half    e_phentsize;            /* 程序头部中一个条目的长度 */
13     Elf32_Half    e_phnum;                /* 程序头部条目个数  */
14     Elf32_Half    e_shentsize;            /* 节头部中一个条目的长度 */
15     Elf32_Half    e_shnum;                /* 节头部条目个数 */
16     Elf32_Half    e_shstrndx;             /* 节头部字符表索引 */
17 } Elf32_Ehdr;
 
  • e_ident[EI_NIDENT]ELF魔数
    • e_ident标识索引如下:

GCC编译器原理(二)------编译原理一:ELF文件(2)_第3张图片

  • 对应的信息是 Magic、Class、Data、Version、OS/ABI 和 ABI Version
  • e_ident[0] - e_ident[3] 包含了ELF文件的魔数,依次是0x7f、'E'、'L'、'F'。注意,任何一个ELF文件必须包含此魔数。
  • e_ident[4] 表示硬件系统的位数,1代表32位,2代表64位。
  • e_ident[5] 表示数据编码方式(字节序),1代表小端排序(最大有意义的字节占有最低的地址),2代表大端排序(最大有意义的字节占有最高的地址)。
  • e_ident[6] 指定ELF文件的主版本号,一般为1。
  • e_ident[7]到e_ident[14]是填充符,通常是0。ELF格式规范中定义这几个字节是被忽略的,但实际上是这几个字节完全可以可被利用。
  • e_ident[7] 为0x21,表示本文件已被感染;或者存放可执行代码。
  • e_type:ELF 文件类型
    • ET_NONE:值为0。未知目标文件格式
    • ET_REL:值为 1。可重定位文件,一般为 .o 文件
    • ET_EXEC:值为2。可执行文件
    • ET_DYN:值为 3。共享目标文件,一般为 .so 文件
    • ET_CORE:值为4。Core 文件(转储格式)
    • ET_LOPROC:值为 0xff00。特定处理器文件
    • ET_HIPROC:值为 0xffff。特定处理器文件
    • ET_LOPROC 和 ET_HIPROC 之间的取值用来标识与处理器相关的文件格式。
  • e_machine:ELF 文件的 CPU 平台属性。相关常量以 EM_ 开头

名称

取值

含义

EM_NONE

0

未指定

EM_M32

1

AT&T WE 32100

EM_SPARC

2

SPARC

EM_386

3

Intel 80386

EM_68K

4

Motorola 68000

EM_88K

5

Motorola 88000

EM_860

7

Intel 80860

EM_MIPS

8

MIPS RS3000

其它值都是保留的。特定处理器的 ELF 名称会使用机器名来进行区分。

  • e_version:目标文件版本,一般为常数 1

名称

取值

含义

EV_NONE

0

非法版本

EV_CURRENT

1

当前版本

  • e_entry:程序入口地址,规定 ELF 程序入口的虚拟地址,操作系统在加载完该程序后从这个地址开始执行进程的指令。可重定位文件一般没有入口地址,则这个值为 0
  • e_phoff:对应 Start of program headers。程序头部表格( Program Header Table)的偏移量(按字节计算)。如果文件没有程序头部表格,可以为 0。
  • e_shoff:对应 Start of section headers。节区头部表格( Section Header Table) 的偏移量(按字节计算)。 如果文件没有节区头部表格,可以为 0。
  • e_flags:ELF 标志位,用来标识一些 ELF 文件平台相关的属性。相关常量的格式一般为 EF_machine_flag,machine 为平台,flag 为标志
  • e_ehsize:ELF 文件头本身的大小(以字节计算),对应 Size of this header
  • e_phentsize:对应 Size of program headers。程序头部表格的表项大小(按字节计算)
  • e_phnum:对应 Number of program headers。程序头部表格的表项数目。可以为 0。
  • e_shentsize:对应 Size of section headers,节区头部表格的表项大小(按字节计算)。
  • e_shnum:对应 Number of section headers,节区头部表格的表项数目。可以为 0。
  • e_shstrndx:节区头部表格中与节区名称字符串表相关的表项的索引。 如果文件没有节区名称字符串表,此参数可以为 SHN_UNDEF。

ELF 头部中大多数字段都是对子头部数据的描述,其意义相对比较简单。值得注意的是某些病毒可能修改字段 e_entry(程序进入点)的值,以指向病毒代码。

4.2 Program header table:程序头表

紧接ELF头部的是程序头表。执行命令:readelf -l a.out

GCC编译器原理(二)------编译原理一:ELF文件(2)_第4张图片

程序头是一个结构数组,包含了 ELF 头表中字段 e_phnum 定义的条目,此结构描述一个段或其他系统准备执行该程序所需要的信息。

结构体位于:/usr/include/elf.h

 1 typedef struct {
 2     Elf32_Word  p_type;               /* 段类型 */
 3     Elf32_Off   p_offset;             /* 段位置相对于文件开始处的偏移量 */
 4     Elf32_Addr  p_vaddr;              /* 段在内存中的地址 */
 5     Elf32_Addr  p_paddr;              /* 段的物理地址 */
 6     Elf32_Word  p_filesz;             /* 段在文件中的长度 */
 7     Elf32_Word  p_memsz;              /* 段在内存中的长度 */
 8     Elf32_Word  p_flags;              /* 段的标记 */
 9     Elf32_Word  p_align;              /* 段在内存中对齐标记 */
10 } Elf32_Phdr;
 

对一个ELF可执行程序而言,一个基本的段是标记 p_type 为 PT_INTERP 的段,它表明了运行此程序所需要的程序解释器(/lib/ld-linux.so.2),实际上也就是动态连接器(dynamic linker)。

  • 最重要的段是标记 p_type 为 PT_LOAD 的段,它表明了为运行程序而需要加载到内存的数据。查看上面实际输入,可以看见有两个可 LOAD 段,第一个为只读可执行(FLg 为 R E ),第二个为可读可写(Flg 为 RW)。
    • 段一包含了文本节 .text ,注意到 ELF 文件头部中程序进入点的值为 0x400430,它会指向节.text在内存中的地址。
    • 段二包含了数据节 .data,此数据节中数据是可读可写的,相对的只读数据节 .rodata 包含在段一中。

4.3 Section:节区

  • 节区中包含目标文件中的所有信息,除了: ELF 头部、程序头部表格、节区头部表格。节区满足以下条件:
    • 目标文件中的每个节区都有对应的节区头部描述它, 反过来, 有节区头部不意味着有节区。
    • 每个节区占用文件中一个连续字节区域(这个区域可能长度为 0)。
    • 文件中的节区不能重叠,不允许一个字节存在于两个节区中的情况发生。
    • 目标文件中可能包含非活动空间( INACTIVE SPACE)。这些区域不属于任何头部和节区,其内容未指定。

很多节区中包含了程序和控制信息。 下面的表中给出了系统使用的节区, 以及它们的类型和属性。

名称

类型

属性

含义

.bss

SHT_NOBITS

SHF_ALLOC +SHF_WRITE

包含将出现在程序的内存映像中的为初始化数据。根据定义,当程序开始执行,系统将把这些数据初始化为 0。 此节区不占用文件空间。

.comment

SHT_PROGBITS

(无)

包含版本控制信息。

.data

SHT_PROGBITS

SHF_ALLOC + SHF_WRITE

这些节区包含初始化了的数据,将出现在程序的内存映像中。

.data1

SHT_PROGBITS

SHF_ALLOC + SHF_WRITE

.debug

SHT_PROGBITS

(无)

此节区包含用于符号调试的信息。

.dynamic

SHT_DYNAMIC

 

此节区包含动态链接信息。节区的属性将包含 SHF_ALLOC 位。是否 SHF_WRITE 位被设置取决于处理器。

.dynstr

SHT_STRTAB

SHF_ALLOC

此节区包含用于动态链接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。

.dynsym

SHT_DYNSYM

SHF_ALLOC

此节区包含了动态链接符号表。

.fini

SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时,系统将安排执行这里的代码。

.got

SHT_PROGBITS

 

此节区包含全局偏移表。

.hash

SHT_HASH

SHF_ALLOC

此节区包含了一个符号哈希表。

.init

SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

此节区包含了可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在开始调用主程序入口之前(通常指 C 语言的 main 函数)执行这些代码。

.interp

SHT_PROGBITS

 

此节区包含程序解释器的路径名。如果程序包含一个可加载的段,段中包含此节区,那么节区的属性将包含 SHF_ALLOC 位,否则该位为 0。

.line

SHT_PROGBITS

(无)

此节区包含符号调试的行号信息,其中描述了源程序与机器指令之间的对应关系。其内容是未定义的。

.note

SHT_NOTE

(无)

此节区中包含注释信息,有独立的格式。

.plt

SHT_PROGBITS

 

此节区包含过程链接表( procedure linkage table)。

.relname

SHT_REL

 

这些节区中包含了重定位信息。如果文件中包含可加载的段,段中有重定位内容,节区的属性将包含 SHF_ALLOC 位,否则该位置 0。传统上 name 根据重定位所适用的节区给定。 例如 .text 节区的重定位节区名字将是: .rel.text 或者 .rela.text。

.relaname

SHT_RELA

 

.rodata

SHT_PROGBITS

SHF_ALLOC

这些节区包含只读数据, 这些数据通常参与进程映像的不可写段。

.rodata1

SHT_PROGBITS

SHF_ALLOC

.shstrtab

SHT_STRTAB

 

此节区包含节区名称。

.strtab

SHT_STRTAB

 

此节区包含字符串, 通常是代表与符号表项相关的名称。如果文件拥有一个可加载的段,段中包含符号串表, 节区的属性将包含 SHF_ALLOC 位,否则该位为 0。

.symtab

SHT_SYMTAB

 

此节区包含一个符号表。如果文件中包含一个可加载的段,并且该段中包含符号表,那么节区的属性中包含SHF_ALLOC 位,否则该位置为 0。

.text

SHT_PROGBITS

SHF_ALLOC + SHF_EXECINSTR

此节区包含程序的可执行指令。

  • 在分析这些节区的时候,需要注意如下事项:
    • 以"."开头的节区名称是系统保留的。 应用程序可以使用没有前缀的节区名称, 以避免与系统节区冲突。
    • 目标文件格式允许人们定义不在上述列表中的节区。
    • 目标文件中也可以包含多个名字相同的节区。
    • 保留给处理器体系结构的节区名称一般构成为:处理器体系结构名称简写 + 节区名称。
    • 处理器名称应该与 e_machine 中使用的名称相同。例如 .FOO.psect 街区是由 FOO 体系结构定义的 psect 节区。
  • 另外,有些编译器对如上节区进行了扩展, 这些已存在的扩展都使用约定俗成的名称,如:.sdata、.tdesc、.sbss、.lit4、.lit8、.reginfo、.gptab、.liblist、.conflict 等等

4.4 Section header table:节区头部表格

执行命令 readelf -S a.out 可以查看到节表头,这里面保存了 ELF 文件中的各种各样的节。节表是 ELF 文件中除了文件头以外最重要的结构,它描述了 ELF 各个 节 的信息,比如每个节的节名、节的长度、在文件中的偏移、读写权限及节的其他属性。即 ELF 文件的节结构就是由节表来决定的,编译器、链接器和装载器都是依靠节表来定位和访问各个节的属性的。节表在 ELF 文件中的位置由 ELF 文件头的"e_shoff"成员决定。

 1 There are 31 section headers, starting at offset 0x19d8:
 2 
 3 Section Headers:
 4   [Nr] Name              Type        Address           Offset    Size              EntSize           Flags  Link  Info  Align
 5   [ 0]                   NULL        0000000000000000  00000000  0000000000000000  0000000000000000         0     0     0
 6   [ 1] .interp           PROGBITS    0000000000400238  00000238  000000000000001c  0000000000000000   A     0     0     1
 7   [ 2] .note.ABI-tag     NOTE        0000000000400254  00000254  0000000000000020  0000000000000000   A     0     0     4
 8   [ 3] .note.gnu.build-i NOTE        0000000000400274  00000274  0000000000000024  0000000000000000   A     0     0     4
 9   [ 4] .gnu.hash         GNU_HASH    0000000000400298  00000298  000000000000001c  0000000000000000   A     5     0     8
10   [ 5] .dynsym           DYNSYM      00000000004002b8  000002b8  0000000000000060  0000000000000018   A     6     1     8
11   [ 6] .dynstr           STRTAB      0000000000400318  00000318  000000000000003d  0000000000000000   A     0     0     1
12   [ 7] .gnu.version      VERSYM      0000000000400356  00000356  0000000000000008  0000000000000002   A     5     0     2
13   [ 8] .gnu.version_r    VERNEED     0000000000400360  00000360  0000000000000020  0000000000000000   A     6     1     8
14   [ 9] .rela.dyn         RELA        0000000000400380  00000380  0000000000000018  0000000000000018   A     5     0     8
15   [10] .rela.plt         RELA        0000000000400398  00000398  0000000000000030  0000000000000018  AI     5    24     8
16   [11] .init             PROGBITS    00000000004003c8  000003c8  000000000000001a  0000000000000000  AX     0     0     4
17   [12] .plt              PROGBITS    00000000004003f0  000003f0  0000000000000030  0000000000000010  AX     0     0     16
18   [13] .plt.got          PROGBITS    0000000000400420  00000420  0000000000000008  0000000000000000  AX     0     0     8
19   [14] .text             PROGBITS    0000000000400430  00000430  0000000000000192  0000000000000000  AX     0     0     16
20   [15] .fini             PROGBITS    00000000004005c4  000005c4  0000000000000009  0000000000000000  AX     0     0     4
21   [16] .rodata           PROGBITS    00000000004005d0  000005d0  0000000000000011  0000000000000000   A     0     0     4
22   [17] .eh_frame_hdr     PROGBITS    00000000004005e4  000005e4  0000000000000034  0000000000000000   A     0     0     4
23   [18] .eh_frame         PROGBITS    0000000000400618  00000618  00000000000000f4  0000000000000000   A     0     0     8
24   [19] .init_array       INIT_ARRAY  0000000000600e10  00000e10  0000000000000008  0000000000000000  WA     0     0     8
25   [20] .fini_array       FINI_ARRAY  0000000000600e18  00000e18  0000000000000008  0000000000000000  WA     0     0     8
26   [21] .jcr              PROGBITS    0000000000600e20  00000e20  0000000000000008  0000000000000000  WA     0     0     8
27   [22] .dynamic          DYNAMIC     0000000000600e28  00000e28  00000000000001d0  0000000000000010  WA     6     0     8
28   [23] .got              PROGBITS    0000000000600ff8  00000ff8  0000000000000008  0000000000000008  WA     0     0     8
29   [24] .got.plt          PROGBITS    0000000000601000  00001000  0000000000000028  0000000000000008  WA     0     0     8
30   [25] .data             PROGBITS    0000000000601028  00001028  0000000000000010  0000000000000000  WA     0     0     8
31   [26] .bss              NOBITS      0000000000601038  00001038  0000000000000008  0000000000000000  WA     0     0     1
32   [27] .comment          PROGBITS    0000000000000000  00001038  0000000000000035  0000000000000001  MS     0     0     1
33   [28] .shstrtab         STRTAB      0000000000000000  000018cc  000000000000010c  0000000000000000         0     0     1
34   [29] .symtab           SYMTAB      0000000000000000  00001070  0000000000000648  0000000000000018        30    47     8
35   [30] .strtab           STRTAB      0000000000000000  000016b8  0000000000000214  0000000000000000         0     0     1
36 Key to Flags:
37   W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
38   I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
39   O (extra OS processing required) o (OS specific), p (processor specific)
 

除了使用 readelf 可以查看节的详细信息外,也可以使用 objdump 来查看 ELF 文件中节,只不过 objdump 只列出 ELF 文件中关键的节,省略了其他辅助节。如下:

GCC编译器原理(二)------编译原理一:ELF文件(2)_第5张图片

ELF 头部中, e_shoff 成员给出从文件头到节区头部表格的偏移字节数; e_shnum 给出表格中条目数目; e_shentsize 给出每个项目的字节数。 从这些信息中可以确切地定位节区的具体位置、长度。

节区头部表格中比较特殊的几个下标如下:

名称

取值

说明

SHN_UNDEF

0

标记未定义的、缺失的、不相关的,或者没有含义的节区引用

SHN_LORESERVE

OXFF00

保留索引的下界

SHN_LOPROC

0XFF00

 

SHN_HIPROC

0XFF1F

保留给处理器特殊的语义

SHN_ABS

OXFFF1

包含对应引用量的绝对取值。这些值不会被重定位所影响

SHN_COMMON

OXFFF2

相对于此节区定义的符号是公共符号。如 FORTRAN 中 COMMON 或者未分配的 C 外部变量。

SHN_HIRESERVE

0XFFFF

保留索引的上界

介于 SHN_LORESERVE 和 SHN_HIRESERVE 之间的表项不会出现在节区头部表中。

节头表结构体在 /usr/include/elf.h 中,结构如下:

 1 typedef struct elf32_shdr {
 2     ELF32_Word      sh_name;        /* 节名 */
 3     ELF32_Word      sh_type;        /* 节的类型 */
 4     ELF32_Word      sh_flags;       /* 节的标志位 */
 5     ELF32_Addr      sh_addr;        /* 节的虚拟地址 */
 6     ELF32_Off       sh_offset;      /* 节偏移 */
 7     ELF32_Word      sh_size;        /* 节的长度 */
 8     ELF32_Word      sh_link;        /* 节连接信息 */
 9     ELF32_Word      sh_info;        /* 节连接信息 */
10     ELF32_Word      sh_addralign;   /* 节地址对齐 */
11     ELF32_Word      sh_entsize;     /* 项的长度 */
12 } ELF32_Shdr;
 
  • sh_name:节名。节名是个字符串,它位于一个叫做 ".shstrtab"的字符串表中。sh_name 是节名字符串在 ".shstrtab"中的偏移
  • sh_type:节的类型。节的名字只在编译和链接的过程中有意义,但它不能真正的表示节的类型。我们可以将一个数据段命名为 ".text",对于编译器和链接器来说,主要决定节的属性是节的类型(sh_type)和节的标志位(sh_flags)。节的类型相关常量以 SHT_ 开头,如下:

名称

取值

说明

SHT_NULL

0

此值标志节区头部是非活动的,没有对应的节区。此节区头部中的其他成员取值无意义。

SHT_PROGBITS

1

此节区包含程序定义的信息,其格式和含义都由程序来解释。

SHT_SYMTAB

2

此节区包含一个符号表。目前目标文件对每种类型的节区都只能包含一个,不过这个限制将来可能发生变化。一般, SHT_SYMTAB 节区提供用于链接编辑(指 ld 而言)的符号,尽管也可用来实现动态链接。

SHT_STRTAB

3

此节区包含字符串表。目标文件可能包含多个字符串表节区。

SHT_RELA

4

此节区包含重定位表项, 其中可能会有补齐内容( addend),例如 32 位目标文件中的 Elf32_Rela 类型。目标文件可能拥有多个重定位节区。

SHT_HASH

5

此节区包含符号哈希表。所有参与动态链接的目标都必须包含一个符号哈希表。目前,一个目标文件只能包含一个哈希表,不过此限制将来可能会解除。

SHT_DYNAMIC

6

此节区包含动态链接的信息。目前一个目标文件中只能包含一个动态节区,将来可能会取消这一限制。

SHT_NOTE

7

此节区包含以某种方式来标记文件的信息。

SHT_NOBITS

8

这种类型的节区不占用文件中的空间,其他方面和 SHT_PROGBITS 相似。 尽管此节区不包含任何字节, 成员 sh_offset 中还是会包含概念性的文件偏移

SHT_REL

9

此节区包含重定位表项, 其中没有补齐( addends),例如 32 位目标文件中的 Elf32_rel 类型。目标文件中可以拥有多个重定位节区。

SHT_SHLIB

10

此节区被保留,不过其语义是未规定的。包含此类型节区的程序与 ABI 不兼容。

SHT_DYNSYM

11

作为一个完整的符号表,它可能包含很多对动态链接而言不必要的符号。因此,目标文件也可以包含一个 SHT_DYNSYM 节区,其中保存动态链接符号的一个最小集合,以节省空间。

SHT_LOPROC

0X70000000

这一段(包括两个边界),是保留给处理器专用语义的。

SHT_HIPROC

OX7FFFFFFF

SHT_LOUSER

0X80000000

此值给出保留给应用程序的索引下界。

SHT_HIUSER

0X8FFFFFFF

此值给出保留给应用程序的索引上界。

其它的节区类型是保留的。

  • sh_flags:节的标志位。节的标志位表示该节在进程虚拟地址空间中的属性,比如是否可写,是否可执行等。相关常量以 SHF_ 开头:
    • SHF_WRITE:值为1,表示该节在进程空间中可写
    • SHF_ALLOC:值为2,表示该节在进程空间中需要分配空间。有些包含指示或控制信息的节不需要在进程空间中被分配空间,它们一般不会有这个标志。像代码段、数据段和 .bss 段都会有这个标志
    • SHF_EXECINSTR:值为4,表示该节在进程空间中可以被执行,一般指代码节
    • SHF_MASKPROC:值为 0xf000000,所有包含于此掩码中的四位都用于处理器专用的语义。
  • sh_addr:节的虚拟地址。如果该节可以被加载,则 sh_addr 为该节被加载后在进程地址空间中的虚拟地址;否则 sh_addr 为0
    • 系统保留段的属性如下表

Name

sh_type

sh_flag

.bss

SHT_NOBITS

SHF_ALLOC + SHF_WRITE

.comment

SHT_PROGBITS

none

.data

SHT_PROGBITS

SHF_ALLOC + SHF_WRITE

.data1

SHT_PROGBITS

SHF_ALLOC + SHF_WRITE

.debug

SHT_PROGBITS

none

.dynamic

SHT_DYNAMIC

SHF_ALLOC + SHF_WRITE

在有些系统下 .dynamic 节可能是只读的,所以没有 SHF_WRITE 标志位

.hash

SHT_HASH

SHF_ALLOC

.line

SHT_PROGBITS

none

.note

SHT_NOTE

none

.rodata

SHT_PROGBITS

SHF_ALLOC

.rodata1

SHT_PROGBITS

SHF_ALLOC

.shstrtab

SHT_STRTAB

none

.strtab

SHT_STRTAB

如果该 ELF 文件中有可装载的段需要用到该字符串表,那么该字符串表也将被装载到进程空间,则有 SHF_ALLOC 标志位

.symtab

SHT_SYMTAB

同字符串表

.text

SHT_PROGBITS

SHF_ALLOC

  • sh_offset:节偏移。如果该节存在于文件中,则表示该节在文件中的偏移;否则无意义。比如 sh_offset 对于 BSS 节来说就没有意义。SHT_NOBITS 类型的节区不占用文件的空间, 因此其 sh_offset 成员给出的是其概念性的偏移。
  • sh_size:此成员给出节区的长度(字节数)。除非节区的类型是 SHT_NOBITS,否则节区占用文件中的 sh_size 字节。类型为 SHT_NOBITS 的节区长度可能非零, 不过却不占用文件中的空间。
  • sh_link 和 sh_info:节连接信息。
    • sh_link 此成员给出节区头部表索引链接。其具体的解释依赖于节区类型。
    • sh_info 此成员给出附加信息,其解释依赖于节区类型。
    • 如果节的类型是与链接相关的(不论是动态链接或静态链接),比如重定位表、符号表等,那么这两个成员所包含的意义如下表所示。对于其他类型的段,这两个成员没有意义。

sh_type

sh_link

sh_info

SHT_DYNAMIC

该节所用的字符串表在节表中的下标

0

SHT_HASH

该节所使用的符号表在节表中的下标

0

SHT_REL

该节所使用的相应符号表在节表中的下标

该重定位表所作用的节在节表中的下标

SHT_RELA

SHT_SYMTAB

操作系统相关的

操作系统相关的

SHT_DNYSYM

other

SHN_UNDEF

0

  • sh_addralign:节地址对齐。
    • 某些节区带有地址对齐约束。例如,如果一个节区保存一个 doubleword, 那么系统必须保证整个节区能够按双字对齐。 sh_addr 对 sh_addralign 取模,结果必须为 0。目前仅允许取值为 0 和 2 的幂次数。数值 0 和 1 表示节区没有对齐约束。
  • sh_entsize:项的长度。有些节包含了一些固定大小的项,比如符号表,它包含的每个符号所占的大小都是一样的。对于这种节,sh_entsize 表示每个项的大小。如果为 0 ,则表示该节不包含固定大小的段。

索引为零(SHN_UNDEF)的节区头部也是存在的,尽管此索引标记的是未定义的节区引用。这个节区的内容固定如下(SHN_UNDEF(0)节区的内容):

字段名称

取值

说明

sh_name

0

无名称

sh_type

SHT_NULL

非活动

sh_flags

0

无标志

sh_addr

0

无地址

sh_offset

0

无文件偏移

sh_size

0

无尺寸大小

sh_link

SHN_UNDEF

无链接信息

sh_info

0

无辅助信息

sh_addralign

0

无对齐要求

sh_entsize

0

无表项

当前的映射如下:

GCC编译器原理(二)------编译原理一:ELF文件(2)_第6张图片

ELF 格式可以比 COFF 格式包含更多的调试信息。在 I386 平台 LINUX 系统下,用命令 file 查看一个 ELF 可执行程序的可能输出是:a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped。

转载于:https://www.cnblogs.com/kele-dad/p/9490380.html

你可能感兴趣的:(GCC编译器原理(二)------编译原理一:ELF文件(2))