【Linux0.11代码分析】09 之 ELF可执行程序02 - Section Headers解析

【Linux0.11代码分析】09 之 ELF可执行程序02 - Section Headers解析

  • 一、ELF概述
  • 二、ELF的组成结构
    • 2.1 ELF header:解析出 section headers 含31个section节和 program headers 含13个segment段
    • 2.2 Section Headers:获取当前程序的31个section节区信息
      • 2.2.1 [30] .shstrtab 字符串表节:保存各section节的字符串名 sh_name
      • 2.2.2 [0] NULL 节:数据为空
      • 2.2.3 [1] .interp 节:指定在程序运行时所要使用的动态链接器路径和名称
      • 2.2.4 [2] .note.gnu.property 节:存储GNU 相关的属性信息
      • 2.2.5 [3] .note.gnu.build-id ELF Note节:存储编译时生成的Build ID,用于快速判断两个二进制文件是否一致
      • 2.2.6 [4] .note.ABI-tag 节:存储支持的操作系统的版本信息
      • 2.2.7 [5] .gnu.hash 节:包含了动态链接器用于快速查找符号的哈希表
      • 2.2.8 [6] .dynsym 节: 包含了动态链接器在程序执行过程中需要使用的外部函数和变量的符号表
      • 2.2.9 [7] .dynstr 字符串表节: 用于存储动态链接器需要的符号名称字符串
      • 2.2.10 [8] .gnu.version 符号表节:存储与符号版本相关的信息
      • 2.2.11 [9] .gnu.version_r 节: 存储版本需求信息
      • 2.2.12 [10] .rela.dyn 节:存储动态重定位表
      • 2.2.13 [11] .rela.plt 节:存储 PLT(Procedure Linkage Table)的动态重定位表
      • 2.2.14 [12] .init 节:存储了在程序启动时需要执行的初始化代码
      • 2.2.15 [13] .plt 节: 用于实现动态链接的延迟绑定机制(Lazy Binding)
      • 2.2.16 [14] .plt.got 节:用于实现动态链接库中全局变量的延迟绑定
      • 2.2.17 [15] .plt.sec 节:用于实现对 PLT 表项中跳转到 GOT 表项地址的保护
      • 2.2.18 [16] .text 节:存放程序代码的二进制指令
      • 2.2.19 [17] .fini 节:存储了程序结束时需要执行的代码
      • 2.2.20 [18] .rodata 节:存储只读数据,如常量
      • 2.2.21 [19] .eh_frame_hdr 节: 存储了 .eh_frame节中的调试信息的压缩版本,可以使调试信息的解析更加高效
      • 2.2.22 [20] .eh_frame 节:存储了异常处理框架信息,用于程序的调试和分析
      • 2.2.23 [21] .init_array 节:存储了在程序开始执行时需要运行的一系列初始化函数
      • 2.2.24 [22] .fini_array 节:存储了在程序退出时需要运行的一系列清理函数
      • 2.2.25 [23] .dynamic 节:存储了动态链接时需要用到的信息,用于实现程序启动时的动态链接机制
      • 2.2.26 [24] .got 节:存储了全局变量或函数的地址
      • 2.2.27 [25] .data 节:存储了程序在运行时需要用到的初始化的全局变量和静态变量的数据
      • 2.2.28 [26] .bss 节:存储了程序中未初始化的全局变量或静态变量的空间
      • 2.2.29 [27] .comment 节:包含了编译器和链接器的一些注释信息
      • 2.2.30 [28] .symtab 节:存储程序中的符号表信息,包括全局变量、函数名和其他符号等
      • 2.2.31 [29] .strtab 节: 存储程序中的字符串表信息
    • 2.3 Program Headers:解析13个segment段 及 对应的虚拟地址和物理地址
    • 2.4 Program Headers:解析13个segment 段和31个section节的映射关系


系列文章如下:

系列文章汇总:《【Linux0.11代码分析】之 系列文章链接汇总(全)》
https://blog.csdn.net/Ciellee/article/details/130510069
.
1.《【Linux0.11代码分析】01 之 代码目录分析》
2.《【Linux0.11代码分析】02 之 bootsect.s 启动流程》
3.《【Linux0.11代码分析】03 之 setup.s 启动流程》
4.《【Linux0.11代码分析】04 之 head.s 启动流程》
5.《【Linux0.11代码分析】05 之 kernel 0号进程初始化 init\main.c 代码分析》
6.《【Linux0.11代码分析】06 之 kernel 1号进程 init() 代码分析》
7.《【Linux0.11代码分析】07 之 kernel execve() 函数 实现原理》
8.《【Linux0.11代码分析】08 之 ELF可执行程序01 - ELF header解析》
9.《【Linux0.11代码分析】09 之 ELF可执行程序02 - Section Headers解析》
10.《【Linux0.11代码分析】10 之 ELF可执行程序03 - Program Headers解析》



本文承接前文:《【Linux0.11代码分析】08 之 ELF可执行程序01 - ELF header解析》

本文,我们来详细分析下 Section Headers 中各section 的含义:


一、ELF概述

二、ELF的组成结构

2.1 ELF header:解析出 section headers 含31个section节和 program headers 含13个segment段

以上内容见:
《【Linux0.11代码分析】08 之 ELF可执行程序01 - ELF header解析》



2.2 Section Headers:获取当前程序的31个section节区信息

Section Headers 描述了所有的section的信息,这是从编译和链接的角度来看ELF文件的,通过它可以找到相应的 .section节的内容,从而把它加载到相应的 segment段区内存中。

当前 程序所包含的 section 节区信息如下 :

CielleeX:~/work/hello$ readelf -a hello
......
Section Headers:
  [Nr] Name              Type             Address           Offset 		Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000 	0000000000000000  0000000000000000   0     	 0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318 	000000000000001c  0000000000000000   A     	 0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000338  00000338	0000000000000030  0000000000000000   A     	 0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000368  00000368 	0000000000000024  0000000000000000   A     	 0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000038c  0000038c	0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003b0  000003b0	0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8	00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000480  00000480	000000000000008f  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000000510  00000510	000000000000000e  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000520  00000520	0000000000000030  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000550  00000550	00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             0000000000000610  00000610	0000000000000018  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000	000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020	0000000000000020  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001040  00001040	0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001050  00001050	0000000000000010  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001060  00001060	0000000000000176  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000011d8  000011d8	000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000	0000000000000022  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002024  00002024	000000000000003c  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002060  00002060	00000000000000cc  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003db8  00002db8	0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc0	0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003dc8  00002dc8	00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb8  00002fb8	0000000000000048  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000	0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004010  00003010	0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003010	000000000000002d  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040	0000000000000378  0000000000000018          29    18     8
  [29] .strtab           STRTAB           0000000000000000  000033b8	00000000000001e1  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  00003599	000000000000011a  0000000000000000           0     0     1

从前面 elf header 解析中,我们能够知道:
section headers 的偏移地址是 14008 bytes,大小为64 bytes,总共包含 31section,也就是共占 31x64=1984bytes
获取所有 section 的命令为: hexdump -C -s 14008 -n 1984 hello

我们先读三个 section 内容出来看下,如下:
在这里插入图片描述


如上,第一个section 均为0,使用 * 省略了,我们把它展开来,如下:

lihaiyan@CielleeX:~/work/hello$ hexdump -C -s 14008 -n 192  hello
第一个section 节区
000036b8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036c8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036d8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036e8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

第二个section 节区:当前section 的name,保存在.shstrtab 的节区中偏移 0x1b 的位置处
000036f81b 00 00 0001 00 00 00  02 00 00 00 00 00 00 00  |................|
00003708  18 03 00 00 00 00 00 00  18 03 00 00 00 00 00 00  |................|
00003718  1c 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003728  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

第三个section 节区:当前section 的name,保存在.shstrtab 的节区中偏移 0x23 的位置处
00003738  23 00 00 00 07 00 00 00  02 00 00 00 00 00 00 00  |#...............|
00003748  38 03 00 00 00 00 00 00  38 03 00 00 00 00 00 00  |8.......8.......|
00003758  30 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |0...............|
00003768  08 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003778
......

section 节结构体信息定义如下:

typedef struct {
	Elf64_Word sh_name; 		// 4 B (B for bytes)
		===>ELF文件中的每个节都有一个唯一的名称,如果sh_name 属性值为 0x00000035,表示该节的名称在节名称字符串表中的第 0x35 个字节处开始。
	
	Elf64_Word sh_type; 		// 4 B
		===============>
		+	sh_type 是一个表示 ELF 文件中每个节的类型的属性值,通常使用 Elf64_Word 类型来存储。其取值范围是由标准定义的一些枚举值,例如:
		+			SHT_NULL:		0,表示该节头部表项无效。
		+			SHT_PROGBITS:	1,表示该节包含程序定义的信息,如代码、数据等。
		+			SHT_SYMTAB:	2,表示该节包含符号表,存储在文件中的全局和局部符号信息。
		+			SHT_STRTAB:	3,表示该节包含字符串表,存储以 NULL 结尾的 ASCII 字符串。
		+			SHT_RELA:		4,表示该节包含重定位条目,为保证程序的可执行性而进行修正。
		+			SHT_HASH:		5,表示该节包含哈希表,用于加速符号查找。
		+			SHT_DYNAMIC:	6,表示该节包含动态连接信息。
		+			SHT_NOTE:		7,表示该节包含一些附加信息,如版本信息。
		+			SHT_REL:		9,表示该节包含重定位条目,不含添加常量值的重定位信息。
		+			SHT_DYNSYM:	11,表示该节包含动态符号表,只包含全局符号信息。
		<===============
	
	Elf64_Xword sh_flags; 		// 8 B
		===============>
		+	sh_flags 是一个表示 ELF 文件中每个节的属性标志值的属性,通常使用 Elf64_Xword 类型来存储。
		+	sh_flags 属性的各个标志位对应的含义如下:
		+		SHF_WRITE 		(0x1):该节包含可写数据。
		+		SHF_ALLOC 		(0x2):该节会被加载到内存中。
		+		SHF_EXECINSTR 	(0x4):该节包含可执行的指令。
		+		SHF_MERGE 		(0x10):该节中的相邻数据可以合并为较大的数据块。
		+		SHF_STRINGS 	(0x20):该节包含以 NULL 结尾的 ASCII 字符串。
		+		SHF_INFO_LINK 	(0x40):该节的 sh_info 字段包含相关信息
		<===============
	
	Elf64_Addr sh_addr; 		// 8 B 表示 ELF 文件中每个节的内存地址或偏移量
	Elf64_Off sh_offset; 		// 8 B 指定了该节在 ELF 文件中的起始位置的偏移量,以便在ELF文件中正确地定位该节的数据
	Elf64_Xword sh_size; 		// 8 B 表示 ELF 文件中每个节的大小
		===============>
		+	对于在进程虚拟地址空间中加载的节(即 sh_flags 属性包含 SHF_ALLOC),sh_size 属性指定了该节在进程虚拟地址空间中占用的总字节数;
		+	对于在文件中存储的节(即 sh_flags 属性不包含 SHF_ALLOC),sh_size 属性指定了该节在 ELF 文件中所占用的总字节数。
		<===============
	
	Elf64_Word sh_link; 		// 4 B	表示 ELF 文件中每个节关联的其他节的索引值
		===============>
		+	它指定了该节关联的其他节的索引值,也就是和该节有依赖关系的其他节的索引号
		+	对于一些特殊的节类型,如符号表节和重定位节,它们通常会通过 sh_link 属性来关联到其他的节。
		+		在符号表节中,sh_link 属性通常指向字符串表节,以便找到该符号名在字符串表中的位置;
		+		在重定位节中,sh_link 属性通常指向需要进行重定位操作的节,以便确定需要修正哪些地址。
		<===============
	
	Elf64_Word sh_info; 		// 4 B 表示 ELF 文件中每个节的附加信息的属性
		===============>
		+	对于符号表节(SHT_SYMTAB)和动态符号表节(SHT_DYNSYM),sh_info 属性指定了该表中局部符号的数量。
		+		局部符号是指只在当前目标文件或共享对象中可见的符号,而不会被其他目标文件或共享对象引用。
		+		全局符号是指可以被其他目标文件或共享对象引用的符号。
		+
		+	对于重定位节(SHT_REL 和 SHT_RELA),sh_info 属性指定了需要进行重定位的符号表在符号表节中的起始索引。
		+		在执行重定位时,程序首先会遍历重定位节中的所有项,确定需要进行重定位的地址,
		+		然后通过该属性指定的符号表来查找需要进行修正的符号。
		<===============
	Elf64_Xword sh_addralign; 	// 8 B	表示 ELF 文件中每个节的对齐方式, 1,则表示该节的数据按字节对齐
		
	Elf64_Xword sh_entsize; 	// 8 B  表示每个节项的大小,即每个节中的元素大小
		===============>
		+	ELF 文件格式中,除了某些特殊的节类型(如 SHT_NULL 和 SHT_NOBITS)外,每个节的大小都是固定的。
		+	对于这些节,sh_entsize 属性就用来表示每个节项的大小,即每个节中的元素大小。
		+
		+	sh_entsize 属性通常使用 Elf64_Xword 类型来存储,它表示每个节项的大小,以字节为单位。
		+	例如,	在符号表节(SHT_SYMTAB 或 SHT_DYNSYM)中,每个符号表项的大小就由 sh_entsize 属性来指定。
		+			在字符串表节(SHT_STRTAB)中,每个字符串的大小也由该属性来确定。
		<===============
} Elf64_Shdr; 					// total size: 64 B

下面,我们对每个节进行分析:


2.2.1 [30] .shstrtab 字符串表节:保存各section节的字符串名 sh_name

之所以优先分析 .shstrtab 节,是因为它保存了所有 section 节区的字符串名 sh_name
先把它的信息读取出来吧:
.shstrtab 节的偏移位置为: 14008 b y t e s + 30 ∗ 64 = 15928 14008 bytes + 30 * 64 = 15928 14008bytes+3064=15928
在这里插入图片描述


我们来对它解析下:

CielleeX:~/work/hello$ hexdump -C -s 15928 -n 64  hello
00003e38  11 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
00003e48  00 00 00 00 00 00 00 00  99 35 00 00 00 00 00 00  |.........5......|
00003e58  1a 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003e68  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003e78

typedef struct {
	Elf64_Word sh_name; 		// 4 B  		// 节名称位于偏移 0x11 处
		---> 00003e3811 00 00 0003 00 00 00  00 00 00 00 00 00 00 00
	
	Elf64_Word sh_type; 		// 4 B			// 03对应SHT_STRTAB类型,表示该节包含字符串表,存储以 NULL 结尾的 ASCII 字符串
		---> 00003e38  11 00 00 0003 00 00 0000 00 00 00 00 00 00 00
	
	Elf64_Xword sh_flags; 		// 8 B			// 无flag属性
		---> 00003e38  11 00 00 00 03 00 00 0000 00 00 00 00 00 00 00】
	
	Elf64_Addr sh_addr; 		// 8 B			// 无虚拟入口
		---> 00003e4800 00 00 00 00 00 00 0099 35 00 00 00 00 00 00
		
	Elf64_Off sh_offset; 		// 8 B			// 该 section 在当前文件中的偏移为 0x00003599
		---> 00003e48  00 00 00 00 00 00 00 0099 35 00 00 00 00 00 00】

	Elf64_Xword sh_size; 		// 8 B			// 大小为 0x011a = 282 bytes
		---> 00003e581a 01 00 00 00 00 00 0000 00 00 00 00 00 00 00
		
	Elf64_Word sh_link; 		// 4 B			// 无相关的 section
		---> 00003e58  1a 01 00 00 00 00 00 0000 00 00 0000 00 00 00
	Elf64_Word sh_info; 		// 4 B			// 无额外的section 信息
		---> 00003e58  1a 01 00 00 00 00 00 00  00 00 00 0000 00 00 00】
		
	Elf64_Xword sh_addralign; 	// 8 B			// 该 section的对齐宽度为 01
		---> 00003e6801 00 00 00 00 00 00 0000 00 00 00 00 00 00 00
		
	Elf64_Xword sh_entsize; 	// 8 B			// 该section 不包含 table,所该字段为0
		---> 00003e68  01 00 00 00 00 00 00 0000 00 00 00 00 00 00 00} Elf64_Shdr; 					// total size: 64 B

通过解析,我们得知:
.shstrtab 节 的内容位于文件偏移 0x00003599 (13721) 处,大小为 282 bytes,我们读出来看看,
如下 ,果然是各个section 节区的sh_name
在这里插入图片描述
比如,当前 sectionsh_name0x11 (17) ,
对应的二进制为: 2e 73 68 73 74 72 74 61 62 00 ,对应的字符串为:.shstrtab

CielleeX:~/work/hello$ hexdump -C -s 13721 -n 282  hello
00003599  00 2e 73 79 6d 74 61 62  00 2e 73 74 72 74 61 62  |..symtab..strtab|
000035a9  00 2e 73 68 73 74 72 74  61 62 00 2e 69 6e 74 65  |..shstrtab..inte|
000035b9  72 70 00 2e 6e 6f 74 65  2e 67 6e 75 2e 70 72 6f  |rp..note.gnu.pro|
000035c9  70 65 72 74 79 00 2e 6e  6f 74 65 2e 67 6e 75 2e  |perty..note.gnu.|
000035d9  62 75 69 6c 64 2d 69 64  00 2e 6e 6f 74 65 2e 41  |build-id..note.A|
000035e9  42 49 2d 74 61 67 00 2e  67 6e 75 2e 68 61 73 68  |BI-tag..gnu.hash|
000035f9  00 2e 64 79 6e 73 79 6d  00 2e 64 79 6e 73 74 72  |..dynsym..dynstr|
00003609  00 2e 67 6e 75 2e 76 65  72 73 69 6f 6e 00 2e 67  |..gnu.version..g|
00003619  6e 75 2e 76 65 72 73 69  6f 6e 5f 72 00 2e 72 65  |nu.version_r..re|
00003629  6c 61 2e 64 79 6e 00 2e  72 65 6c 61 2e 70 6c 74  |la.dyn..rela.plt|
00003639  00 2e 69 6e 69 74 00 2e  70 6c 74 2e 67 6f 74 00  |..init..plt.got.|
00003649  2e 70 6c 74 2e 73 65 63  00 2e 74 65 78 74 00 2e  |.plt.sec..text..|
00003659  66 69 6e 69 00 2e 72 6f  64 61 74 61 00 2e 65 68  |fini..rodata..eh|
00003669  5f 66 72 61 6d 65 5f 68  64 72 00 2e 65 68 5f 66  |_frame_hdr..eh_f|
00003679  72 61 6d 65 00 2e 69 6e  69 74 5f 61 72 72 61 79  |rame..init_array|
00003689  00 2e 66 69 6e 69 5f 61  72 72 61 79 00 2e 64 79  |..fini_array..dy|
00003699  6e 61 6d 69 63 00 2e 64  61 74 61 00 2e 62 73 73  |namic..data..bss|
000036a9  00 2e 63 6f 6d 6d 65 6e  74 00                    |..comment.|
000036b3

2.2.2 [0] NULL 节:数据为空

第一个section 节,数据全为空,如下:

lihaiyan@CielleeX:~/work/hello$ hexdump -C -s 14008 -n 64 hello
000036b8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036c8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036d8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036e8  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036f8

2.2.3 [1] .interp 节:指定在程序运行时所要使用的动态链接器路径和名称

.interp 存储了一个以 NULL 结尾的字符串,用于指定在程序运行时所要使用的动态链接器路径和名称。

ELF 文件加载时,如果该文件需要依赖某些共享库,那么系统会自动载入并链接这些库。
此时系统需要查找动态链接器,以便在运行时解析这些共享库的符号引用。
动态链接器的路径和名称通常是由 .interp 节来指定的。

在大多数情况下,.interp 节只包含一个字符串 "/lib/ld-linux.so.2",表示使用 GNU 的动态链接器。
但是,在某些情况下,用户可以通过修改 .interp 节来指定其它的动态链接器路径和名称。

.interp 节的类型是 SHT_PROGBITS,其 sh_flags 属性标志位 SHF_ALLOC、SHF_EXECINSTR,即该节的数据在运行时是可执行的,并且需要被载入内存。

我们把.interp 节的内容打印出来,它的位置位于 14008 + 64 = 14072 byte

CielleeX:~/work/hello$ hexdump -C -s 14072 -n 64 hello
000036f8  1b 00 00 00 01 00 00 00  02 00 00 00 00 00 00 00  |................|
00003708  18 03 00 00 00 00 00 00  18 03 00 00 00 00 00 00  |................|
00003718  1c 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003728  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003738

每个 section header 中包含 10 个条目,每个条目占用 4 字节进行描述,
因为当前 elf 文件是小端,所以所有的字节都以小端模式进行解析,section header解析:

字段类型value注释
sh_name 4Byte1b 00 00 00section name被保存在 .shstrtab中,偏移地址为 0x1b
sh_type 4Byte01 00 00 00 01 对应PROGBITS 类型,表示当前类型为程序数据
sh_flags 8Byte02 00 00 00 00 00 00 0002 对应 SHF_ALLOC,表示程序加载时需要向内核 申请内存
sh_addr 8Byte18 03 00 00 00 00 00 00程序的虚拟地址入口为 0x00000318
sh_offset 8Byte18 03 00 00 00 00 00 00section 在当前文件中的偏移为 0x00000318
sh_size 8Byte1c 00 00 00 00 00 00 00sectionsize0x0000001c
sh_link 4Byte00 00 00 00无相关的 section
sh_info 4Byte00 00 00 00无额外的section 信息
sh_addralign 8Byte01 00 00 00 00 00 00 00该 section的对齐宽度为 01
sh_entsize 8Byte00 00 00 00 00 00 00 00该section 不包含 table,所该字段为0

可以看到,

  1. 当前section 节区名保存存偏移0x1b 处,对应十六进制为 【2e 69 6e 74 65 72 70 00】, 名为.interp
    000035a9 00 2e 73 68 73 74 72 74 61 62 00 【2e 69 6e 74 65 |..shstrtab..inte|
    000035b9 72 70 00】 2e 6e 6f 74 65 2e 67 6e 75 2e 70 72 6f |rp..note.gnu.pro|

  2. .interp 的数据,保存在文件第 0x318的位置处,总长度为0x1c,我们把这一块的数据打印出来看一下:
    在这里插入图片描述

可以看出,它保存的内容为:/lib64/ld-linux-x86-64.so.2, 是编译时的 ld 链接库的名字

2.2.4 [2] .note.gnu.property 节:存储GNU 相关的属性信息

.note.gnu.property 用于存储 GNU 属性(GNU Properties)信息。
GNU 工具链中,通过向 ELF 文件注入 .note.gnu.property 节来实现各种功能,比如增加某些特定的属性信息,以及在编译过程中生成一些辅助信息。

.note.gnu.property 节通常包含多个属性条目(Property Entry),每个条目由一个 magic number 和一个或多个属性项组成。
其中,magic number 用于标识这个属性条目的类型,而属性项则是一些键值对形式的数据,用于存储具体的属性信息。
不同类型的条目具有不同的属性项,可以通过读取 .note.gnu.property 节来获取特定类型的属性信息。

我们把.note.gnu.property 节的内容打印出来,它的位置位于 14008 + 2x64 = 14136 byte
在这里插入图片描述


我们来对它解析下:

CielleeX:~/work/hello$ hexdump -C -s 14136 -n 64  hello
00003738  23 00 00 0007 00 00 00】  【02 00 00 00 00 00 00 00|#...............|	
		// 0x07:该节包含一些附加信息,如版本信息
		// 0x02: 该节会被加载到内存中
0000374838 03 00 00 00 00 00 00】  【38 03 00 00 00 00 00 00|8.......8.......|
		// 虚拟入口地址为: 0x0338
		// 该 section 在当前文件中的偏移为 0x00003338
0000375830 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  |0...............|
		// 该section 大小为 0x30  (48bytes)
00003768  08 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003778

从上得知.note.gnu.property 节 的数据保存在 0x0338 (824)偏移处, 大小为48 bytes, 读取数据如下:
在这里插入图片描述

.note.gnu.property 的结构体定义如下 :

struct elf_property_note {
    Elf64_Word n_namesz;  // 名称长度		4bytes
    Elf64_Word n_descsz;  // 描述信息长度	4bytes
    Elf64_Word n_type;    // 类型			4bytes
    	===========>
    	+	0x05:GNU 版本属性(GNU_PROPERTY_TYPE_GNU_ABI_TAG),用于指定编译时所使用的 ABI 版本。
		+	0x10:GNU 标志属性(GNU_PROPERTY_TYPE_GNU_BUILD_ID),用于存储由 ld --build-id 生成的 Build ID。
		+	0x20:GNU 扩展属性(GNU_PROPERTY_TYPE_GNU_PROPERTY),用于存储其他类型的属性信息。
		<===========
    char         n_name[]; // 名称			
    char         n_desc[]; // 描述信息		
};
各字段含义如下:
	n_namesz:属性名称字符串的长度(以字节为单位),不包括结尾的 NULL 字符。
	n_descsz:属性描述信息的长度(以字节为单位)。
	n_type:该条目的类型,通常是一个特定的 magic number。
	n_name:属性名称字符串,以 NULL 结尾。
	n_desc:属性的描述信息,可以是任意二进制数据。

来对它进行解析下:

CielleeX:~/work/hello$ hexdump -C -s 824 -n 48  hello
00000338  04 00 00 00 20 00 00 00  05 00 00 00 47 4e 55 00  |.... .......GNU.|
00000348  02 00 00 c0 04 00 00 00  03 00 00 00 00 00 00 00  |................|
00000358  02 80 00 c0 04 00 00 00  01 00 00 00 00 00 00 00  |................|
00000368

struct elf_property_note {
    Elf64_Word n_namesz;  // 名称长度		4bytes
    	---> 0000033804 00 00 0020 00 00 00  05 00 00 00 47 4e 55 00	// n_namesz = 4 bytes
    Elf64_Word n_descsz;  // 描述信息长度	4bytes
    	---> 00000338  04 00 00 0020 00 00 0005 00 00 00 47 4e 55 00	// n_descsz = 0x20 (32bytes)
    Elf64_Word n_type;    // 类型			4bytes
    	---> 00000338  04 00 00 00 20 00 00 0005 00 00 0047 4e 55 00  // 0X5: 指定编译时所使用的 ABI 版本
    char         n_name[]; // 名称	
    	---> 00000338  04 00 00 00 20 00 00 00  05 00 00 0047 4e 55 00// n_name[0] = GNU	
    char         n_desc[]; // 描述信息	
    	---> 0000034802 00 00 c0】 【04 00 00 00】  【03 00 00 00】 【00 00 00 00---> 0000035802 80 00 c0】 【04 00 00 00】  【01 00 00 00】 【00 00 00 00// 键:0xc0000002 = 0x00000004	表示的是 .note.gnu.build-id 节的内容为 0x04, 用于唯一标识与该节相关联的可执行程序或共享库
			---> Build ID 是 GNU 工具链提供的一个特性,用于在编译或链接过程中
			---> 将一个固定长度的哈希值(默认为 SHA-1 哈希算法)注入到生成的 ELF 可执行文件或共享库中。
			---> 这个哈希值可以通过读取 .note.gnu.build-id 节来获取,从而判断两个二进制文件是否具有相同的编译环境和源代码版本。
		// 键:0x00000003 = 0x00000000	表示的是 .note.gnu.gold-version 节的内容为0x0
		// 键:0xc0008002 = 0x00000004 	表示的是 GNU 系统工具链的特定属性之一:GNU_PROPERTY_X86_ISA_1_USED, 用于指示 ELF 文件中所使用的 x86 ISA 版本
			---> 0 表示未使用任何特定的 x86 ISA 特性;	1 表示使用 SSE2 指令集;
			---> 2 表示使用 SSE3 指令集;				3 表示使用 SSSE3 指令集;
			---> 4 表示使用 SSE4.1 指令集;				5 表示使用 SSE4.2 指令集;
			---> 6 表示使用 AVX 指令集;					7 表示使用 AVX2 指令集。
		// 键:0x00000000 = 0x00000001 表示的是 GNU 系统工具链的特定属性之一:GNU_PROPERTY_X86_ISA_1_NEEDED, 指示 ELF 文件所必须使用的最低 x86 ISA 版本, 也就是 使用 SSE2 指令集
};

2.2.5 [3] .note.gnu.build-id ELF Note节:存储编译时生成的Build ID,用于快速判断两个二进制文件是否一致

.note.gnu.build-id 是 ELF 二进制文件中的一个特殊节 (section),用于存储编译时生成的构建 ID。
GNU Binutils 工具链使用 Build ID 技术来唯一标识每个可执行文件和共享库,这有助于在不同系统间轻松比较二进制文件的版本。

.note.gnu.build-id节中,如果存在该节,则会包含一个 ELF note 头,其名字是 "GNU"
在这个 ELF note 的描述信息(note desc)中,会包含一个 16 字节的构建 ID(Build ID)
Build ID 的生成方式是对可执行文件或共享库的所有内容计算散列值,因此可以保证对于同一份代码、同一编译环境生成的构建 ID 是唯一的。
通过比较两个 ELF 文件的 .note.gnu.build-id 节中的 Build ID,可以快速判断两个二进制文件是否一致。

我们把.note.gnu.property 节的内容打印出来,它的位置位于 14008 + 3x64 = 14200 byte
在这里插入图片描述

CielleeX:~/work/hello$  hexdump -C -s 14200 -n 64  hello
00003778  36 00 00 00 07 00 00 00  02 00 00 00 00 00 00 00  |6...............|
0000378868 03 00 00 00 00 00 00】  【68 03 00 00 00 00 00 00|h.......h.......|
		// 虚拟地址为: 0x0368
		// 该 section 在当前文件中的偏移为 0x0368
0000379824 00 00 00 00 00 00 0000 00 00 00 00 00 00 00  |$...............|
		// 大小为0x24,36bytes
000037a8  04 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000037b8

从上得知.note.gnu.build-id 节 的数据保存在 0x0368 (872)偏移处, 大小为36 bytes, 读取数据如下:
在这里插入图片描述

.note.gnu.build-id 节是一个 ELF note,其头部信息 (note header) 和描述信息 (note desc) 的结构体定义如下:

// .note.gnu.build-id ELF note header
struct elf_note {
    uint32_t n_namesz; // Name size
    uint32_t n_descsz; // Content size
    uint32_t n_type;   // Type of the note
};

// Description of .note.gnu.build-id ELF note
struct build_id_note {
    uint8_t  bld_id[16]; // Build ID content (16 bytes)
    char     name[];     // Note name
};

其中,elf_note 结构体定义了 ELF note 的头部信息。它包含三个成员变量:

  • n_namesz:名字的大小,指定了该 ELF note 名字的长度(不包括末尾的 ‘\0’ 字符)。
  • n_descsz:内容的大小,指定了该 ELF note 描述信息的长度。
  • n_type:类型,指定了该 ELF note 描述信息的类型。

build_id_note 结构体定义了.note.gnu.build-id 的描述信息。它包含两个成员变量:

  • name:名字,指定了该 ELF note 的名字。在 .note.gnu.build-id 节中,该名字通常被设置为 “GNU”。
  • bld_id:内容,指定了该 ELF note 描述信息的具体内容,即 16 字节的构建 ID。

我们来解析看下:

CielleeX:~/work/hello$ hexdump -C -s 872 -n 36  hello
00000368  04 00 00 00 14 00 00 00  03 00 00 00 47 4e 55 00  |............GNU.|
00000378  5d 4e 34 d6 bc 89 06 f6  0f bd fe 41 a9 65 4a 2c  |]N4........A.eJ,|
00000388  d9 ef 59 37                                       |..Y7|
0000038c

// .note.gnu.build-id ELF note header
struct elf_note {
    uint32_t n_namesz; // Name size = 0x4
    	---> 0000036804 00 00 0014 00 00 00  03 00 00 00 47 4e 55 00
    uint32_t n_descsz; // Content size = 0x14
    	---> 00000368  04 00 00 0014 00 00 0003 00 00 00 47 4e 55 00
    uint32_t n_type;   // Type of the note = 0x3
    	---> 00000368  04 00 00 00 14 00 00 0003 00 00 0047 4e 55 00
};

// Description of .note.gnu.build-id ELF note
struct build_id_note {
	char     name[];     // Note name = GNU
		---> 00000368  04 00 00 00 14 00 00 00 03 00 00 0047 4e 55 00// GNU 
    uint8_t  bld_id[16]; // Build ID content (16 bytes)
   	 	---> 000003785d 4e 34 d6 bc 89 06 f6  0f bd fe 41 a9 65 4a 2c】
};

可以看出 build_id5d 4e 34 d6 bc 89 06 f6 0f bd fe 41 a9 65 4a 2c


2.2.6 [4] .note.ABI-tag 节:存储支持的操作系统的版本信息

.note.ABI-tag (Advanced Binary Interface tag) 是 ELF 文件格式中的一种特殊类型的段(section
它包含了描述目标文件与其所在操作系统 ABI (Application Binary Interface) 版本信息的数据。

目标文件的编译器和链接器通常会在构建时自动添加该段以实现与操作系统 ABI 版本的兼容性。

.note.ABI-tag 的结构体定义如下:

typedef struct {
    Elf64_Word n_namesz;   /* Length of the note's name */
    Elf64_Word n_descsz;   /* Length of the note's descriptor */
    Elf64_Word n_type;     /* Type of the note */
} Elf64_Nhdr;

n_namesz 和 n_descsz 分别表示段名及其描述信息的大小,以字节为单位;n_type 表示该段的类型,通常为 NT_ABI。

一些常见的 ABI 版本类型值及其用途如下:
+ NT_BUILD_ID:指定目标文件的构建 ID。该值与 .note.gnu.build-id 段类似。
+ NT_GNU_ABI_TAG: 指定目标文件所采用的 GNU ABI 版本号,以及相关额外的信息。
	该值包含以下字段:
		abi: 一个整数,表示 GNU ABI 的版本号。当前最新版本号为 0x010100,即 1.1。
		abi_desc: 描述此目标文件所需的其他 ABI 特定信息的字符串。
		note_tag: 该值固定为 "GNU".

我们把.note.gnu.property 节的内容打印出来,它的位置位于 14008 + 4x64 = 14264 byte
在这里插入图片描述
可以看出,.note.ABI-tag 的内容位于 0x038c (908) 处,大小为 0x20 (32byte)

在这里插入图片描述

解析如下:

0000038c  04 00 00 00          ; n_namesz = 4 (Name size)
00000390  10 00 00 00          ; n_descsz = 16 (Content size)
00000394  01 00 00 00          ; n_type = 1 (Type of the note)

00000398  47 4e 55 00          ; Note name: "GNU"
0000039c  00 00 00 00          ; Alignment padding
000003a0  03 00 00 00          ; ABI version number (0x00000003)		ABI版本号为 0x00000003
000003a4  02 00 00 00          ; Type of the object file (0x00000002)	对象文件类型为 0x00000002
000003a8  00 00 00 00          ; Padding								对齐

2.2.7 [5] .gnu.hash 节:包含了动态链接器用于快速查找符号的哈希表

.gnu.hash 包含了动态链接器用于快速查找符号的哈希表。
相比 .hash 段,.gnu.hash 段提供了更高效的符号查找机制,能够在较短的时间内快速定位符号。

我们把.gnu.hash 节的内容打印出来,它的位置位于 14008 + 5x64 = 14328 byte
在这里插入图片描述
可以看出,它位于 0x03b0处,大小为0x24, 分析方法同上,此处就不分析了。

.gnu.hash 段由以下结构体进行描述:

typedef struct {
    Elf32_Word nbuckets;      /* Number of buckets */
    Elf32_Word symndx;        /* First symbol index */
    Elf32_Word maskwords;     /* Number of Bloom filter words */
    Elf32_Word *bloom;        /* The Bloom filter */
    Elf32_Word *buckets;      /* The bucket array */
    Elf32_Word *hashval;      /* The hash value array */
} Elf32_GnuHash;

typedef struct {
    Elf64_Word nbuckets;      /* Number of buckets */
    Elf64_Word symndx;        /* First symbol index */
    Elf64_Word maskwords;     /* Number of Bloom filter words */
    Elf64_Word *bloom;        /* The Bloom filter */
    Elf64_Word *buckets;      /* The bucket array */
    Elf64_Word *hashval;      /* The hash value array */
} Elf64_GnuHash;

其中,

  • nbuckets 表示哈希表的桶数量。
  • symndx 表示第一个符号的索引值。
  • maskwords 表示使用的布隆过滤器(Bloom filter)的单词数量(即 32 位字数)。
  • bloom 表示指向布隆过滤器的指针。
  • buckets 表示指向桶数组的指针。
  • hashval 表示指向哈希值数组的指针。

2.2.8 [6] .dynsym 节: 包含了动态链接器在程序执行过程中需要使用的外部函数和变量的符号表

用于存储动态链接器需要使用的符号信息, 该段包含了动态链接器在程序执行过程中需要使用的外部函数和变量的符号表

我们把.dynsym 节的内容打印出来,它的位置位于 14008 + 6x64 = 14392 byte
在这里插入图片描述
可以看出,它位于 0x03d8 (984)处,大小为0xa8(168bytes)
在这里插入图片描述

.dynsym 的结构体定义:

typedef struct {
    Elf64_Word st_name;       /* Symbol name (string tbl index) */  4B
    unsigned char st_info;    /* Symbol type and binding */			1B
    unsigned char st_other;   /* Symbol visibility */				1B
    Elf64_Section st_shndx;   /* Section index */					2B
    Elf64_Addr st_value;      /* Symbol value */					8B
    Elf64_Xword st_size;      /* Symbol size */						8B
} Elf64_Sym;

其中,各字段的含义如下:

  • st_name 表示该符号的名称在字符串表中的索引,对应于 .dynstr 段中的字符串。
  • st_info 包含两个部分:符号类型和绑定属性。
  • st_other 用于表示符号的其他属性,如可见性。
  • st_shndx 表示该符号所属的节表索引
  • st_value 表示该符号在地址空间中的值。
  • st_size 表示该符号所占空间的大小。

需要注意的是,在使用 .dynsym 段时,通常需要结合 .dynstr 和 .plt、.got 等段来进行使用,以达到链接共享库和分发程序等目的。

解析如下:

第一个符号表项(位于偏移量 0x00 处)全为 0,表示该符号未定义。

第二个符号表项(位于偏移量 0x10 处)如下:
	st_name 值为 0x00000012,在字符串表中对应字符串 "printf"。
	st_value 值为 0,表示该符号在该文件中未定义。
	st_size 值为 0,表示该符号的大小为 0。
	st_info 值为 0x12,其中高 4 位表示该符号的类型为函数(值为 0x02),低 4 位表示该符号的绑定属性为本地可见(值为 0x00)。
	st_other 值为 0,表示该符号的其他属性为默认值。
	st_shndx 值为 0,表示该符号所属的节为未定义。

第三个符号表项(位于偏移量 0x22 处)如下:
	st_name 值为 0x00000012,在字符串表中对应字符串 "scanf"。
	st_value 值为 0,表示该符号在该文件中未定义。
	st_size 值为 0,表示该符号的大小为 0。
	st_info 值为 0x12,其中高 4 位表示该符号的类型为函数(值为 0x02),低 4 位表示该符号的绑定属性为本地可见(值为 0x00)。
	st_other 值为 0,表示该符号的其他属性为默认值。
	st_shndx 值为 0,表示该符号所属的节为未定义。

第四个符号表项(位于偏移量 0x66 处)如下:
	st_name 值为 0x00000020,在字符串表中对应字符串 "GLOBAL_OFFSET_TABLE"。
	st_value 值为 0x000000000000004a,表示该符号在虚拟地址空间中的地址为 0x4a。
	st_size 值为 0,表示该符号的大小为 0。
	st_info 值为 0x11,其中高 4 位表示该符号的类型为对象(值为 0x01),低 4 位表示该符号的绑定属性为全局可见(值为 0x01)。
	st_other 值为 0,表示该符号的其他属性为默认值。
	st_shndx 值为 0,表示该符号所属的节为未定义。

第五个符号表项(位于偏移量 0x78 处)如下:
	st_name 值为 0x00000020,在字符串表中对应字符串 "_IO_stdin_used"。
	st_value 值为 0x0000000000000075,表示该符号在虚拟地址空间中的地址为 0x75。
	st_size 值为 0,表示该符号的大小为 0。
	st_info 值为 0x10,其中高 4 位表示该符号的类型为对象(值为 0x01),低 4 位表示该符号的绑定属性为全局可见(值为 0x01)。
	st_other 值为 0,表示该符号的其他属性为默认值。
	st_shndx 值为 0,表示该符号所属的节为未定义。

最后两个字节(位于偏移量 0x80 处)全为 0,表示 .dynsym 段解析结束。

2.2.9 [7] .dynstr 字符串表节: 用于存储动态链接器需要的符号名称字符串

.dynstr 节是一个字符串表节,用于存储动态链接器需要的符号名称字符串。
在 ELF 文件中,该节通常紧随 .dynsym 节之后,因为 .dynsym 节中的每个符号条目都包含一个指向对应符号名称在 .dynstr 节中的偏移量。

.dynstr 节的数据类型不是固定大小的,它只是一系列以 null 字符(‘\0’)结尾的字符串按顺序排列而成的字符数组。
在解析 .dynsym 节时,动态链接器通过读取 .dynstr 节中相应偏移量处的字符串来获取符号名称。

我们把.dynstr 节的内容打印出来,它的位置位于 14008 + 7x64 = 14456 byte
在这里插入图片描述

可以看出,它位于 0x04801152)处,大小为0x8f143 bytes
在这里插入图片描述


2.2.10 [8] .gnu.version 符号表节:存储与符号版本相关的信息

.gnu.version 节是 GNU 风格的版本符号表节,用于在 ELF 文件中存储与符号版本相关的信息。
通过使用 .gnu.version 节,我们可以将每个符号分配到不同的版本组中,并为每个版本组分配一个唯一的版本号。

.gnu.version 节由两部分组成:版本数组和版本需求数组。
其中,版本数组是一个包含所有版本号的字符串数组,它在整个 ELF 文件中只出现一次,并且以 null 字符(‘\0’)结尾。
而版本需求数组则包含一个或多个版本需求条目,每个条目包含一个符号的版本需求信息。

我们把.gnu.version 节的内容打印出来,它的位置位于 14008 + 8x64 = 14520 byte
在这里插入图片描述
可以看出,它位于 0x0510 处,大小为0x0e,分析方法同上,就不一一分析了

其结构体定义如下:

struct Elf64_Verdef {
    Elf64_Half vd_version;    // 版本号
    Elf64_Half vd_flags;      // 标识位
    Elf64_Half vd_ndx;        // 版本索引
    Elf64_Half vd_cnt;        // 与此版本项相关联的符号数
    Elf64_Word vd_hash;       // 此版本项关联哈希表项的指针
    Elf64_Word vd_aux;        // 指向版本定义信息(Elf32_Verdaux)数组的指针
    Elf64_Word vd_next;       // 指向下一个版本定义条目的指针
};

2.2.11 [9] .gnu.version_r 节: 存储版本需求信息

.gnu.version_r 节是 ELF 文件中用于存储版本需求信息的节。

这些版本需求信息描述了一个 DSO(动态共享对象) 文件需要哪些版本和符号,以及它们所在的位置等信息。
这些信息是在链接时由生成 DSO 的编译器和链接器自动生成的。

.gnu.version_r 节通常由多个版本需求条目组成。
每个版本需求条目都包含了一个 DSO 文件和一个或多个版本需求描述符,以指定该 DSO 文件需要哪些版本和符号。

我们把.gnu.version 节的内容打印出来,它的位置位于 14008 + 9x64 = 14584 byte
在这里插入图片描述
可以看出,它位于 0x0520 处,大小为0x30,分析方法同上,就不一一分析了

.gnu.version_r 节的版本需求描述符的结构体定义如下:

struct Elf64_Vernaux {
    Elf64_Word vna_hash;    // 与该描述符关联的符号名哈希值
    Elf64_Half vna_flags;   // 描述符类型标志
    Elf64_Half vna_other;   // 其他描述符信息
    Elf64_Word vna_name;    // 与该描述符关联的符号名在字符串表中的偏移量
    Elf64_Word vna_next;    // 指向下一个版本需求描述符的偏移量
};

.gnu.version_r 节中某个 DSO 文件的版本需求条目的结构体定义:

struct Elf64_Verneed {
    Elf64_Half vn_version;  // 版本号
    Elf64_Half vn_cnt;      // 该 DSO 文件需求的版本数
    Elf64_Word vn_file;     // 与该需求条目关联的 DSO 文件名在字符串表中的偏移量
    Elf64_Word vn_aux;      // 指向版本需求描述符(Elf32_Vernaux)数组的偏移量
    Elf64_Word vn_next;     // 指向下一个版本需求条目的偏移量
};

.gnu.version_r 节和 .gnu.version 节一样,都需要编译器和链接器的支持来进行使用和管理。


2.2.12 [10] .rela.dyn 节:存储动态重定位表

.rela.dyn 这个节主要是用来存储动态重定位表的,它是基于 ELF 所采用的重定位机制实现的。

在编译时,由于符号的地址是不确定的,所以需要进行重定位处理,因此会生成一张重定位表来记录符号的位置以及需要做的重定位操作。
在动态链接时,这张表会被加载到内存中,并最终被用来对符号表中的符号进行重定位。

.rela.dyn 节中的重定位表项也称为重定位条目(relocation entry),它的结构体定义如下:

struct Elf64_Rela {
    Elf64_Addr r_offset;    // 该条目所对应的符号地址,即需要被重定位的位置
    Elf64_Word r_info;      // 指定要进行重定位的符号以及重定位类型
    Elf64_Sword r_addend;   // 需要被加上或减去的常数值,用来计算出新的符号值
};

r_offset 是需要被重定位的位置,
r_info 指定了要进行重定位的符号以及重定位类型,
r_addend 则是需要被加上或减去的常数值,用来计算出新的符号值

我们把.rela.dyn 节的内容打印出来,它的位置位于 14008 + 10x64 = 14648 byte
在这里插入图片描述
可以看出,它位于 0x0550 处,大小为0xc0,分析方法同上,就不一一分析了


2.2.13 [11] .rela.plt 节:存储 PLT(Procedure Linkage Table)的动态重定位表

.rela.plt 这个节主要是用来存储 PLTProcedure Linkage Table)的动态重定位表,它同样是基于 ELF 所采用的重定位机制实现的。

在动态链接时,编译器为了实现延迟绑定(lazy binding)以及减少重定位表的大小,会在 .plt 节中生成一张 PLT 表来进行优化。
该表中每个条目负责调度并执行某个特定的函数,
当这个函数第一次被调用时,PLT 中的条目会将控制权转移到 GOTGlobal Offset Table)条目,并更新 GOT 条目中的地址。
在此之后,对该函数的所有调用都会直接跳转到 GOT 条目,从而实现延迟绑定的功能。

.rela.plt 节则记录了需要进行动态重定位的 PLT 条目的信息,这些信息包括 PLT 条目的偏移量、需要进行重定位的符号表索引以及重定位类型等。
具体而言,.rela.plt 节中的重定位表项的结构体定义如下:

struct Elf64_Rela {
    Elf64_Addr r_offset;    // 该条目所对应的符号地址,即需要被重定位的位置
    Elf64_Word r_info;      // 指定要进行重定位的符号以及重定位类型
    Elf64_Sword r_addend;   // 需要被加上或减去的常数值,用来计算出新的符号值
};

我们把.rela.plt 节的内容打印出来,它的位置位于 14008 + 11x64 = 14712 byte
在这里插入图片描述
可以看出,它位于 0x0610 处,大小为0x18,分析方法同上,就不一一分析了


2.2.14 [12] .init 节:存储了在程序启动时需要执行的初始化代码

. init 节存储了在程序启动时需要执行的初始化代码,
这个节通常包含了一些需要在 main 函数之前运行的预处理代码,包括初始化全局变量、申请动态内存等。

ELF 文件被加载到内存中运行时,操作系统会自动在第一次执行 main 函数之前执行 .init 节中的代码,以保证程序能够正确运行。
因此,程序员可以利用 .init 节来进行各种初始化操作,从而提高程序的可靠性和安全性。

.init 节中存储的初始代码的结构体定义如下:

typedef void (*Elf64_Init)(void);

struct Elf64_Init_Array {
    Elf64_Init *init_array; // 指向一组指针数组,每个指针指向一个函数入口地址
    Elf64_Word  init_array_sz; // init_array 数组长度
};

其中,ELF32_INIT 定义了函数指针类型,它指向一个没有参数和返回值的函数;
Elf32_Init_Array 结构体则包含了两个字段:

  • init_array:一个指向函数指针数组的指针,每个指针都指向一个实现了 .init 功能的函数入口地址;
  • init_array_szinit_array 数组的长度,即该数组中指针的个数。

ELF 文件被加载到内存中并准备执行时,操作系统会找到 .init 节,然后遍历其中的 init_array 数组,按顺序依次执行其中存储的函数。这些函数就是用来实现在 main 函数之前应该执行的一系列初始化操作的。

.init 节只有在动态链接模式下才会被用到,在静态链接模式下是不需要使用 .init 节来实现初始化的。

我们把.init 节的内容打印出来,它的位置位于 14008 + 12x64 = 14776 byte
在这里插入图片描述

可以看出,它位于 0x1000 (4096) 处,大小为0x1b (27)
在这里插入图片描述

CielleeX:~/work/hello$ hexdump -C -s 4096 -n 27  hello
00001000  f3 0f 1e fa 48 83 ec 08  48 8b 05 d9 2f 00 00 48  |....H...H.../..H|
00001010  85 c0 74 02 ff d0 48 83  c4 08 c3                 |..t...H....|
0000101b

将 .init 节的内容转化为如下汇编代码:

00001000: f3 0f 1e fa         endbr64
00001004: 48 83 ec 08         sub    rsp, 0x8
00001008: 48 8b 05 d9 2f 00 00 mov    rax, QWORD PTR [rip+0x2fd9]        # 0x200088
0000100f: 48 85 c0            test   rax, rax
00001012: 74 02               je     0x16
00001014: ff d0               call   rax
00001016: 48 83 c4 08         add    rsp, 0x8
0000101a: c3                  ret    

解释如下:

  • f3 0f 1e fa
    指令 endbr64 表示“将一个前缀字节用于指示下一个指针或调用目标地址是不是位于与代码段不同的内存区域。
    “ 这条指令已经在特斯拉架构中被广泛使用,它是一种新的前缀指令,用于支持指向函数指针的内联 asm,
    在新的特斯拉 CPU 中具有显著的性能优势。

  • 48 83 ec 08
    指令 sub rsp, 0x8 将栈指针向下移动 8 个字节,为后续代码的局部变量分配空间。

  • 48 8b 05 d9 2f 00 00
    指令mov rax, QWORD PTR [rip+0x2fd9]RIP 寄存器中指向下一条指令的地址加上偏移量 0x2fd9 所指向的内存地址的值(即一个地址值)赋给RAX 寄存器,该地址值对应的是 .got.plt 节中保存的一个函数的地址。

  • 48 85 c0
    指令 test rax, raxRAX 执行逻辑与操作,并将结果设置到 CPU 标志寄存器中,这里的目的是检查 RAX 是否为 0。

  • 74 02:指令 je 0x16RAX 等于 0 时跳转到 0x16 处。

  • ff d0:指令 call rax 调用 RAX 寄存器中保存的函数。

  • 48 83 c4 08:指令 add rsp, 0x8 将栈指针移回原来的位置,释放为局部变量分配的空间。

  • c3:指令 ret 返回主调函数。

总体来说,这段数据是用于初始化程序的状态或执行一些必要的操作。
其中调用的函数地址是在 .got.plt 节中进行了重定位的,这样可以避免在 ELF 文件中直接暴露函数地址,增加程序的安全性。


2.2.15 [13] .plt 节: 用于实现动态链接的延迟绑定机制(Lazy Binding)

. plt 节是 ELF 文件中的一个节,它主要用于实现动态链接的延迟绑定机制(Lazy Binding)。

具体而言,当一个程序调用一个动态链接库中的函数时,系统并不会立即将程序中的函数调用指向到动态链接库中真正的函数代码上,
而是先将这个调用指向到 .plt 节中的一个占位符函数中,
然后等到第一次真正调用该函数时再进行动态重定位,即将占位符函数指向到真正的函数代码上。

. plt 节的结构体定义如下:

typedef Elf32_Addr (*Elf32_Lookup)(Elf32_Sym *symtab, const char *strtab, Elf32_Addr rel_addr);

struct Elf64_Plt_Entry {
    Elf64_insn  insn1;        // 两条机器码指令,实现了对 GOT 表项的加载和跳转
    Elf64_insn  insn2;
    Elf64_Addr  plt_addr;     // PLT 表中的偏移地址,用于在 LD_PRELOAD 中进行函数劫持
    Elf64_Addr  got_addr;     // 对应的 GOT 中的地址,用于实现延迟绑定功能
    Elf64_Addr  sym_addr;     // 相应的符号表中的地址,用于后续的动态重定位
    Elf64_Word  sym_index;    // 符号表中的索引值,表示该函数的符号表入口
    Elf64_Addr  jmp_addr;     // 实际跳转到的地址,与 got_addr 相同,用于重定位
};

其中,Elf32_Lookup 定义了函数指针类型,它指向一个实现了符号查找功能的函数;

Elf32_Plt_Entry 结构体则包括了以下字段:

  • insn1: PLT 表中对应的第一条机器指令,用于加载并跳转到 GOT 表中的相应项;
  • insn2: PLT 表中对应的第二条机器指令,用于实现 lazy binding 机制中的动态重定位;
  • plt_addr: PLT 表项在 .plt 节中的偏移地址;
  • got_addr: 对应的 GOT 表项的地址,该地址最初指向一个占位符函数(也称为延迟绑定函数)的地址;
  • sym_addr: 相应函数在符号表中的地址;
  • sym_index: 相应函数在符号表中的索引值;
  • jmp_addr: 最终跳转到的函数地址,通常会与 got_addr 相同,在动态重定位的时候将 got_addr 指向真正的函数入口地址即可。

2.2.16 [14] .plt.got 节:用于实现动态链接库中全局变量的延迟绑定

.plt.got 节是一个特殊的节,它用于实现动态链接库中全局变量的延迟绑定。

当程序中引用一个动态链接库中的全局变量时,
系统并不会立即将这个引用指向动态链接库中真正的变量地址,而是先将这个引用指向 .plt.got 节中的一个占位符变量地址,
然后等到第一次真正使用该全局变量时再进行动态重定位,即将占位符变量的地址指向真正的变量地址上。

. plt.got 节的结构体定义如下:

struct Elf64_Plt_Got_Entry {
    Elf64_Addr  jmp_slot;    // 对应的 PLT 表项中的 jmp_addr 地址
    Elf64_Addr  value;       // 对应的全局变量地址,在首次引用时需要进行动态重定位
};

其中,Elf64_Plt_Got_Entry 结构体包括了以下两个字段:

  • jmp_slot: 对应的 PLT 表项中的 jmp_addr 地址;
  • value: 对应的全局变量地址,初始时指向一个占位符变量地址,在首次引用时需要进行动态重定位。

在程序首次访问动态链接库中的全局变量时,系统会对 .plt.got 节中对应的占位符变量进行动态重定位,将其指向真正的全局变量地址上。由于在动态链接库的生命周期内,一个全局变量只需要进行一次动态重定位即可,所以 .plt.got 节中的占位符变量地址是不需要像 . plt 表那样按需生成的。


2.2.17 [15] .plt.sec 节:用于实现对 PLT 表项中跳转到 GOT 表项地址的保护

.plt.sec 节是一个可选的节,它用于实现对 PLT 表项中跳转到 GOT 表项地址的保护。

由于在动态链接库加载时,攻击者可以通过篡改全局变量或符号表来修改 GOT 表项中的地址,从而实现代码劫持等攻击。
为了避免这种攻击,一种常见的方法是使用 .plt.sec 节将 PLT 表项中的跳转指令保护起来,使得攻击者无法篡改其中的地址。

. plt.sec 节的结构体定义如下:

struct Elf64_Plt_Sec_Entry {
    Elf64_Addr  insn1;    // 原始的第一条机器码指令
    Elf64_Addr  insn2;    // 原始的第二条机器码指令
    Elf64_Addr  insn3;    // JMP 指令
    Elf64_Word  pad;      // 对齐用的填充字节,大小根据机器架构而异
};

其中,Elf64_Plt_Sec_Entry 结构体包括了以下四个字段:

  • insn1: PLT 表项中原始的第一条机器码指令;
  • insn2: PLT 表项中原始的第二条机器码指令;
  • insn3: JMP 指令,用于跳转到对应的 GOT 表项地址;
  • pad: 对齐用的填充字节,大小根据机器架构而异。

在正常的动态链接操作中,PLT 表项和 GOT 表项是由静态链接器 (ld) 自动生成并填充的,
因此只要静态链接器的实现足够安全,就不必特地使用 .plt.sec 节来保护 PLT 表项。
但是为了防止动态链接库被攻击者篡改或替换,.plt.sec 节可以作为一种附加的安全措施,能够提高动态链接库的安全性。


2.2.18 [16] .text 节:存放程序代码的二进制指令

在 ELF 文件中,.text 节是一个必须存在的节,它用于存放程序代码的二进制指令。

具体而言,编译器会将源码编译成二进制指令,并将这些指令存储到 .text 节中。
在程序执行时,操作系统会从 .text 节中读取指令并依次执行,从而实现程序功能。

struct Elf64_Text_Entry {
    unsigned char  *data;    // 指向 .text 节中的数据指针
    Elf64_Word      size;    // .text 节中的数据大小,以字节为单位
    Elf64_Addr      addr;    // .text 节的虚拟地址
};

其中,Elf64_Text_Entry 结构体包括了以下三个字段:

  • data: 指向 .text 节中的数据指针;
  • size: .text 节中的数据大小,以字节为单位;
  • addr: .text 节的虚拟地址。

.text 节是只读的,因为它包含了程序的二进制代码,如果允许对其进行写入操作可能会破坏程序的内部逻辑。


2.2.19 [17] .fini 节:存储了程序结束时需要执行的代码

.fini 是一个 ELF 文件中的特殊节(section),它存储了程序结束时需要执行的代码。
当程序正常退出时,动态加载器会在 .fini 节中查找指令,并执行这些指令,以执行一些清理工作和释放资源等操作。

.fini 节与.init 节的作用相反,.init 节在程序初始化时执行,.fini 节则在程序结束时执行。


2.2.20 [18] .rodata 节:存储只读数据,如常量

.rodata 是一个 ELF 文件中的节(section),它存储了只读数据,包括字符串常量、全局常量等。

.rodata 节通常位于代码段和数据段之间,也可以放在一个单独的只读数据段中。

.rodata 节是一个只包含只读数据的段,在内存中只能读取该段的内容,无法修改。
.rodata 节的类型为 SHT_PROGBITS,标志(flags)中包含了 SHF_ALLOCSHF_WRITE 标志,表示该节在内存中是可读写的。


2.2.21 [19] .eh_frame_hdr 节: 存储了 .eh_frame节中的调试信息的压缩版本,可以使调试信息的解析更加高效

.eh_frame_hdr 存储了 .eh_frame 节中的调试信息的压缩版本,可以使调试信息的解析更加高效。
.eh_frame 节存储了程序在执行过程中用到的信息,包括函数的返回地址、局部变量地址、异常处理等,用于程序的调试和分析。

ELF 文件中,.eh_frame_hdr 节位于 .eh_frame 节之前,并且与 .eh_frame 节相同的对齐方式。
.eh_frame_hdr 节包含了一张表格,用于描述 .eh_frame 节中的数据结构和压缩方式,以便更快速地解析出.eh_frame 节中的信息。


2.2.22 [20] .eh_frame 节:存储了异常处理框架信息,用于程序的调试和分析

.eh_frame 是一个 ELF 文件中的节(section),它存储了异常处理框架(Exception Handling Frame)信息,用于帮助程序在运行时定位函数调用链和栈帧信息,并实现 C++ 异常处理机制。

.eh_frame 节通常位于代码段和数据段之间,也可以放在一个单独的只读数据段中。

.eh_frame 节是一个只包含只读数据的段,在内存中只能读取该段的内容,无法修改。
.eh_frame 节的类型为 SHT_PROGBITS,标志(flags)中包含了 SHF_ALLOCSHF_WRITE 标志,表示该节在内存中是可读写的。


2.2.23 [21] .init_array 节:存储了在程序开始执行时需要运行的一系列初始化函数

.init_array 存储了在程序开始执行时需要运行的一系列初始化函数(init 函数)。
这些函数通常由编译器自动生成,在程序开始执行时按照数组顺序调用,用于进行一些初始化工作,如设置全局变量、打开日志文件等。

.init_array 节是一个只包含可执行代码的段,在内存中可以被读取和执行,但不允许写入操作。

.init_array 节中存储了一系列需要在程序开始执行时调用的初始化函数的指针,这些函数由编译器自动生成,并按照数组顺序存储在 .init_array 中,程序执行时会按顺序调用这些函数。

.init_array 节通常与其他初始化相关的节(如 .init、.ctors 等)配合使用,实现程序开始前的各种初始化工作。

2.2.24 [22] .fini_array 节:存储了在程序退出时需要运行的一系列清理函数

.fini_array 是一个 ELF 文件中的节,它存储了在程序退出时需要运行的一系列清理函数(fini 函数)。
这些函数通常由编译器自动生成,在程序退出时按照数组顺序调用,用于进行一些清理工作,如关闭日志文件、释放内存等。

.fini_array 节是一个只包含可执行代码的段,在内存中可以被读取和执行,但不允许写入操作。


2.2.25 [23] .dynamic 节:存储了动态链接时需要用到的信息,用于实现程序启动时的动态链接机制

.dynamic 存储了动态链接时需要用到的信息,用于实现程序启动时的动态链接机制(Dynamic Linking)。
动态链接是指在程序运行时将依赖的库文件加载到内存中,并进行符号解析和重定位,这样可以减小程序的体积,提高可维护性。

.dynamic 节是一个只包含只读数据的段,在内存中只能读取该段的内容,无法修改。


2.2.26 [24] .got 节:存储了全局变量或函数的地址

.got 是一个 ELF 文件中的节,全称为 Global Offset Table,它存储了全局变量或函数的地址。

在程序编译时,如果有对全局变量或函数的引用,编译器会把它们的地址写入 .got 节中。
当程序执行到这些代码段时,由于全局变量或函数的地址还没有确定,所以使用的是 .got 中的地址,而不是实际地址。
在程序执行时,动态链接器会将 .got 节中的地址替换为实际地址,从而实现全局变量或函数的动态链接。

.got 节是一个只包含地址的段,在内存中可以被读取和写入操作。


2.2.27 [25] .data 节:存储了程序在运行时需要用到的初始化的全局变量和静态变量的数据

.data 存储了程序在运行时需要用到的初始化的全局变量和静态变量的数据。
这些数据在程序启动时被复制到进程的数据段中,并在整个程序的执行过程中保持不变。

.data 节是一个包含已初始化数据的段,在内存中可以被读取、修改和执行。

我们把.data 节的内容打印出来,它的位置位于 14008 + 25x64 = 15608 byte
在这里插入图片描述
可以看出,它位于 0x3000 (12288) 处,大小为0x10 (16), 内容如下:
在这里插入图片描述

2.2.28 [26] .bss 节:存储了程序中未初始化的全局变量或静态变量的空间

.bss 全称为 Block Started by Symbol,它存储了程序中未初始化的全局变量或静态变量的空间。

在程序编译时,如果有未初始化的全局变量或静态变量,编译器会把它们的大小信息记录在 .bss 节中。
当程序加载到内存时,操作系统会分配一段对应大小的空间,并把该空间清零,然后更新 .bss 节的地址信息。
因为未初始化的全局变量或静态变量默认值都是 0,所以这些空间不需要在文件中占用存储空间。

.bss 节是一个只包含空间大小信息的段,在内存中不能被读取和写入操作。


2.2.29 [27] .comment 节:包含了编译器和链接器的一些注释信息

.comment 包含了编译器和链接器的一些注释信息。

在程序编译或链接时,如果使用了 -g 选项,编译器和链接器会在 ELF 文件中添加一个 .comment 节,其中包含了一些注释信息。
这些注释信息主要用于调试和识别文件的来源。

.comment 节是一个只包含注释信息的段,其内容格式由编译器和链接器自己决定,通常为 ASCII 码文本。


2.2.30 [28] .symtab 节:存储程序中的符号表信息,包括全局变量、函数名和其他符号等

.symtab 用于存储程序中的符号表信息,包括全局变量、函数名和其他符号等。
符号表中的每个符号都有一个对应的符号表项,该表项是一个结构体,包含符号的名称、类型、大小等信息。
在链接时,链接器会把所有被引用的符号都加入到符号表中,并建立符号表和代码中的符号之间的映射关系。

在 ELF 文件中,.symtab 数据段的头部是一个 ELF 符号表头部(Elf32_SymElf64_Sym),用于描述符号表中每个符号表项的大小、数量和存储位置等信息。

每个符号表项本身也是一个结构体,其定义如下:

typedef struct {
    Elf32_Word st_name;     /* Symbol name (string table index) */
    Elf32_Addr st_value;    /* Symbol value */
    Elf32_Word st_size;     /* Symbol size */
    unsigned char st_info;  /* Symbol type and binding */
    unsigned char st_other; /* Symbol visibility */
    Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;

typedef struct {
    Elf64_Word st_name;     /* Symbol name (string table index) */
    unsigned char st_info;  /* Symbol type and binding */
    unsigned char st_other; /* Symbol visibility */
    Elf64_Section st_shndx; /* Section index */
    Elf64_Addr st_value;    /* Symbol value */
    Elf64_Xword st_size;    /* Symbol size */
} Elf64_Sym;

这些字段的含义如下:

  1. st_name: 符号名字符串在字符串表中的偏移量。通过该偏移量可以在字符串表中查找到符号名。
  2. st_value: 符号在内存中的地址或值。对于函数,通常是函数入口地址;对于变量,通常是变量在内存中的地址或初始值。
  3. st_size: 符号的大小或长度(以字节为单位)。
  4. st_info: 符号类型和绑定信息,编码方式如下:
	BITS    TYPE   DESCRIPTION
	0-3     Type   Symbol type (ET_NONE, ET_REL, ET_EXEC, etc)
	4-7     Bind   Symbol binding (STB_LOCAL, STB_GLOBAL, etc)

总之,.symtab 数据段中存储了程序中所有的符号及其相关信息,提供了在链接期间进行符号解析和重定位的基础。


2.2.31 [29] .strtab 节: 存储程序中的字符串表信息

.strtabELF 格式可执行文件中的一种数据段,用于存储程序中的字符串表信息。
ELF 文件中,各个标记、符号等都需要使用字符串来表示,通过将这些字符串集中存储在 .strtab 数据段中,可以减小可执行文件的大小。

.strtab 数据段中的字符串是以 null 结尾的 C 字符串(即以'\0'字符结尾的字符串),每个字符串以一个偏移量(offset)来进行索引。
因此,在 ELF 文件解析过程中,需要通过访问该数据段来获取相关字符串信息,比如符号表中的符号名称等。
字符串表中的每个字符串则是按照顺序存放的。

总而言之,.strtab 数据段是一个常用的数据段,用于存储程序中的字符串信息,提供了在 ELF 文件解析过程中获取字符串信息的基础。


2.3 Program Headers:解析13个segment段 及 对应的虚拟地址和物理地址


详见: 《【Linux0.11代码分析】10 之 ELF可执行程序03 - Program Headers解析》


描述了各个 segment 段的虚拟地址 和 物理地址,及其对应的权限 RW

Program Headers:
  Type           Offset             VirtAddr           PhysAddr				FileSiz            MemSiz            Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040  	0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318	0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000	0x0000000000000628 0x0000000000000628  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000	0x00000000000001e5 0x00000000000001e5  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000 	0x000000000000012c 0x000000000000012c  R      0x1000
  LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8	0x0000000000000258 0x0000000000000260  RW     0x1000
  DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8	0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338	0x0000000000000030 0x0000000000000030  R      0x8
  NOTE           0x0000000000000368 0x0000000000000368 0x0000000000000368	0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338	0x0000000000000030 0x0000000000000030  R      0x8
  GNU_EH_FRAME   0x0000000000002024 0x0000000000002024 0x0000000000002024	0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000	0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8	0x0000000000000248 0x0000000000000248  R      0x1

2.4 Program Headers:解析13个segment 段和31个section节的映射关系


详见: 《【Linux0.11代码分析】10 之 ELF可执行程序03 - Program Headers解析》


可以看出,.init_array .fini_array .dynamic .got .data .bss 这个section 节是保存在第6个 segment 段中

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .plt.sec .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11
   12     .init_array .fini_array .dynamic .got




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/22034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

极狐(GitLab) 重磅发布新产品「极狐星」,让研发效能看得清,算得准,成就企业精英效能管理

在研发驱动业务增长的今天&#xff0c;越来越多的研发管理者发现&#xff1a; 总是觉得研发资源不够用&#xff1f; 如何用数据衡量研发效能&#xff1f; 如何定位软件交付瓶颈&#xff1f; 怎样管理并预警项目状态&#xff1f; 想尽早发现代码泄露风险怎么办&#xff1f;…

CleanMyMac X如何下载解锁完整版本?

这是一款很受到mac用户喜爱的清理软件。不仅清理文件的步骤十分简单&#xff0c;电脑小白用户也可以高效清理Mac电脑。作为一款全方位保护电脑的软件&#xff0c;CleanMyMac已经不满足于只做简单的Mac清理工具&#xff0c;而是为mac用户提供更多的实用功能&#xff1a;优化系统…

Redis三种集群模式

一、引言 Redis有三种集群模式&#xff0c;第一个就是主从模式&#xff0c;第二种“哨兵”模式&#xff0c;第三种是Cluster集群模式&#xff0c;第三种的集群模式是在Redis 3.x以后的版本才增加进来的&#xff0c;我们今天就来说一下Redis第一种集群模式&#xff1a;主从集群模…

Halcon 算子 select_shape_std 和 select_shape_xld区别

文章目录 1 select_shape_std 算子介绍2 select_shape_xld算子介绍3 select_shape_std 和 select_shape_xld区别4 Halcon 算子的特征 Features 列表介绍1 select_shape_std 算子介绍 select_shape_std (Operator) Name select_shape_std — Select regions of a given shape.Si…

【嵌入式烧录刷写文件】-2.4-移动Intel Hex中指定地址范围内的数据

案例背景&#xff08;共5页精讲&#xff09;&#xff1a; 有如下一段Hex文件&#xff0c;将源地址范围0x9100-0x9104中数据&#xff0c;移动至一个“空的&#xff0c;未填充的”目标地址范围0xA000-0xA004。 :2091000058595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F70717…

【C++】类和对象(上)

【C】类和对象 前言遗漏的部分内联函数使用注意 语法糖auto循环&#xff08;&#xff1a;&#xff09; 正篇&#xff1a;面向对象&#xff08;上&#xff09;面向对象的思路类和对象stuct的升级对象class封装&#xff08;private protect public&#xff09;定义和声明分离this…

Vue3通透教程【十二】TS类型声明优势

文章目录 &#x1f31f; 写在前面&#x1f31f; 上篇文章解惑&#x1f31f; JS函数中的隐患&#x1f31f; 函数中的类型&#x1f31f; 写在最后 &#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章&#xff0c;应粉丝要求开始更新 V…

2023-5-19-Debug和Release到底有多少不同?

&#x1f37f;*★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* &#x1f37f; &#x1f4a5;&#x1f4a5;&#x1f4a5;欢迎来到&#x1f91e;汤姆&#x1f91e;的csdn博文&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f49f;&#x1f49f;喜欢的朋友可以关注一下&#xf…

垃圾站养殖场除臭杀菌解决方案

养殖场和垃圾站都会产生大量的有机废气和垃圾&#xff0c;这些废气和垃圾会产生难闻的臭味&#xff0c;影响周围环境和居民健康。这些地方又是病菌和细菌的滋生地&#xff0c;这些细菌和病菌会对人类和动物的健康造成威胁。除臭杀菌系统可以杀灭这些细菌和病菌&#xff0c;也可…

【Java|golang】1080. 根到叶路径上的不足节点--dfs

给你二叉树的根节点 root 和一个整数 limit &#xff0c;请你同时删除树中所有 不足节点 &#xff0c;并返回最终二叉树的根节点。 假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit&#xff0c;则该节点被称之为 不足节点 &#xff0c;需要被删…

回溯法【2-5】

假设一个推销员问题由下图定义&#xff0c;用回溯法求解 从1号结点出发的相应最短巡回路径&#xff08;每个顶点刚好到达一次&#xff09;。若用bestL表示搜索过程中产生的当前最优解&#xff0c;剪枝函数 L 设计为&#xff1a; L 已走过的路径长度 当前结点相关的最短边 所…

10:00进去,10:05就出来了,这问的也太变态了···

从外包出来&#xff0c;没想到死在另一家厂子了。 自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到5月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有个兄弟内推…

【Win32】资源文件(对话框),逆向对话框回调函数,消息断点(附带恶意软件源码)

之前在学习windows编程的时候已经写过对话框的创建了&#xff0c;其中包括了对话框的分类&#xff0c;原理等等&#xff0c;大家可以去看一下&#xff1a;【windows编程之对话框】对话框原理&#xff0c;对话框的创建。原理今天就讲的不是很多了&#xff0c;直接给大家给出步骤…

《Go专家编程(第2版)》书评

首先感谢官方的肯定&#xff0c;让我在【图书活动第四期】的活动中获得了《Go专家编程(第2版)》这本书&#xff0c;以下是从我的观点对这本书的书评 文章目录 前言书籍部分读者评价总结 前言 很高兴有机会写一篇关于《Go专家编程&#xff08;第2版&#xff09;》的书评。大致读…

APIO2023 游记

GDOI 和 GDKOI 的游记都咕咕咕了&#xff0c;而且都炸了&#xff0c;APIO 的游记提前发&#xff0c;就是要破釜沉舟。 我是线上选手。 Day -7 我们原题检测&#xff0c;阿克了&#xff0c;毕竟是原题&#xff0c;虽然有两道博弈论黑题确实挺毒瘤的。 教练让我做 APIO2012 的…

产品经理必读丨如何找准产品定位?

我们都知道&#xff0c;当一款新产品开始立项之前&#xff0c;势必需要经过谨慎的市场调研才能整合资源启动新的项目&#xff0c;但除此之外&#xff0c;作为产品经理还需要做好一件关键的事情——找准产品在市场中的定位。 什么是产品定位 百度百科对产品定位的解释是非常准确…

【STM32】基础知识 第十六课 窗口看门狗 WWDG 深入浅出

【STM32】基础知识 第十六课 窗口看门狗 WWDG 深入浅出 概述窗口看门狗 (WWDG)WWDG_SR 状态寄存器WWDG 配置与使用使用 WWDG 进行故障检测案例 概述 在嵌入式开发中, 可靠性和稳定性是至关重要的. 这就是为什么许多单片机, 比如 STM32, 提供了窗口看门狗 (Window Watchdog, WW…

k8s部署dashboard

1.查看k8s集群版本 kubectl version 2.在github中查看k8s对应的ui版本 Releases kubernetes/dashboard GitHub 3.下载对应版本的dashboard yaml文件 wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml 4.更改yaml文件配置 …

HTB-Agile

HTB-Agile 信息收集80端口漫长的兔子洞之旅 立足www-data -> corumcorum -> edwardsedwards -> root 信息收集 80端口 漫长的兔子洞之旅 我注意到系统为我分配了一个session&#xff0c;是以eyj开头的。 拿去jwt.io看看。 额&#xff0c;可能后面会用先留在这&#…

多线程-程序、进程、线程与并行、并发的概念

多线程 本章要学习的内容&#xff1a; 专题1&#xff1a;相关概念的理解专题2&#xff1a;多线程创建方式一&#xff1a;继承Thread类专题3&#xff1a;多线程创建方式二&#xff1a;实现Runnable接口专题4&#xff1a;Thread类的常用方法专题5&#xff1a;多线程的优点、使用…