由于一直对 elf 文件充满好奇心,链接器如何将 elf 文件链接成可执行文件的?可执行文件为什么可执行?操作系统到底是怎么将该文件加载并让 CPU 去执行的?这些真要弄明白,其实并不容易,因此,首先就从 elf 文件的格式开始探索吧!!
ELF(Executable and Linking Format)是一种文件格式,该格式的文件可执行,可链接。可执行是站在操作系统的角度的,可链接是站在链接器(Linker)的角度。可执行指的是操作系统能够加载该文件到内存中,并让 CPU 去执行该文件中的指令。可链接指的是链接器能够将分离的这种格式的文件链接(拼图)成为一个可以让操作系统加载的文件。
实际上,让 cpu 执行与 链接器链接 这两个动作是非常复杂的,而 elf 格式的目的就是辅助操作系统或者链接器去完成这些动作,不然随意整一格式的文件,操作系统与链接器肯定是不认识的(除非自己写操作系统,自己写编译器、链接器,然后自己定出自己的格式)。
经常使用的 ELF 文件类型
操作系统使用的是 ubuntu 18.04,其他 linux 发行版皆可
编译工具链:gcc
实验源码,只编译不链接,本文章探究可重定位文件(可执行文件与之类似)。
gcc -c my_test.c
unsigned int data_0 = 0xffff0000;
unsigned int data_1 = 0xffff1111;
static int data_bss;
int func_0(void);
int func_1(void);
可执行文件是将可重定位文件中相同类型的的 section 聚合到一起形成 Segment,这样有利于操作系统的加载。
从上到下,包含了 elf 头、程序头表、各种类型的 section、节头表,从箭头可以看出,头表中指向了中间的各个节
section 本文叫做 节
头的作用就是为了表明,我是 elf 格式的文件,是一种身份的表示,使用 hexdump 工具将文件中的数据按照 16 进制的方式显示出来,如下
hexdump -n 512 -C my_test.o //打印前 512 字节,并显示出对应 ascii 码,高字节在后,如下
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 d0 01 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 09 00 08 00 |....@.....@.....|
00000040 00 00 ff ff 11 11 ff ff 00 47 43 43 3a 20 28 55 |.........GCC: (U|
00000050 62 75 6e 74 75 20 37 2e 35 2e 30 2d 33 75 62 75 |buntu 7.5.0-3ubu|
00000060 6e 74 75 31 7e 31 38 2e 30 34 29 20 37 2e 35 2e |ntu1~18.04) 7.5.|
00000070 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 01 00 00 00 04 00 f1 ff 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 00 |................|
000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 03 00 02 00 00 00 00 00 00 00 00 00 |................|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 03 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 09 00 00 00 01 00 03 00 00 00 00 00 00 00 00 00 |................|
00000100 04 00 00 00 00 00 00 00 00 00 00 00 03 00 05 00 |................|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000120 00 00 00 00 03 00 04 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 12 00 00 00 11 00 02 00 |................|
00000140 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000150 19 00 00 00 11 00 02 00 04 00 00 00 00 00 00 00 |................|
00000160 04 00 00 00 00 00 00 00 00 68 65 6c 6c 6f 2e 63 |.........hello.c|
00000170 00 64 61 74 61 5f 62 73 73 00 64 61 74 61 5f 30 |.data_bss.data_0|
00000180 00 64 61 74 61 5f 31 00 00 2e 73 79 6d 74 61 62 |.data_1...symtab|
00000190 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
000001a0 61 62 00 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |ab..text..data..|
000001b0 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00 2e 6e 6f |bss..comment..no|
000001c0 74 65 2e 47 4e 55 2d 73 74 61 63 6b 00 00 00 00 |te.GNU-stack....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200
这就是 elf 文件中前 512 字节的内容,文件头数据结构定义如下
/* linux 内核 elf.h 中定义 */
typedef __u64 Elf64_Addr;
typedef __u16 Elf64_Half;
typedef __s16 Elf64_SHalf;
typedef __u64 Elf64_Off;
typedef __s32 Elf64_Sword;
typedef __u32 Elf64_Word;
typedef __u64 Elf64_Xword;
typedef __s64 Elf64_Sxword;
#define EI_NIDENT 16
typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT];// 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Elf64_Half e_type;// 01 00 ,elf 文件类型 1 为重定位文件
Elf64_Half e_machine; // 3e 00
Elf64_Word e_version; // 01 00 00 00
Elf64_Addr e_entry; // 00 00 00 00 00 00 00 00 程序入口地址,可重定位文件不可执行,此时还没有入口地址
Elf64_Off e_phoff;// 00 00 00 00 00 00 00 00 由于是可重定位文件,暂时不存在程序头表,因此为 0
Elf64_Off e_shoff;// d0 01 00 00 00 00 00 00 节头表在该文件中的偏移 0x1d0
Elf64_Word e_flags; // 00 00 00 00
Elf64_Half e_ehsize; // 40 00 elf header 大小,也就是在该文件中的占用也就是该结构的占用 sizeof(elf64_hdr)
Elf64_Half e_phentsize; // 00 00 程序头表大小,可重定位文件中没有,因此为 0
Elf64_Half e_phnum; // 00 00 程序头表个数,可重定位文件中没有,因此为 0
Elf64_Half e_shentsize; //40 00 节头表大小,0x40 字节
Elf64_Half e_shnum; // 09 00 节头表个数
Elf64_Half e_shstrndx; // 08 00 节字符串表在 节头表中的下标
} Elf64_Ehdr;
据上述分析,ELF 文件头中的内容已经按照数据结构的定义一一对照起来了,可以使用 readelf -h my_test.o 进一步查看,如下
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 // e_ident 16字节
类别: ELF64 // e_ident[4] == 02
数据: 2 补码,小端序 (little endian) // e_ident[5] == 01
版本: 1 (current) // e_ident[6] == 01
OS/ABI: UNIX - System V // e_machine
ABI 版本: 0
类型: REL (可重定位文件) // e_type
系统架构: Advanced Micro Devices X86-64 // e_machine
版本: 0x1 // e_version
入口点地址: 0x0 // e_entry
程序头起点: 0 (bytes into file) // e_entry
Start of section headers: 464 (bytes into file) // e_shoff 0x1d0
标志: 0x0
本头的大小: 64 (字节) // e_ehsize 0x40
程序头大小: 0 (字节) // e_phentsize 0x00
Number of program headers: 0 // e_phnum 0x00
节头大小: 64 (字节) // e_shentsize 0x40
节头数量: 9 // e_shnum 0x9
字符串表索引节头: 8 // 0x8
有了 elf head ,可以知道节头部表偏移,得到了 节头部表的位置,解析节头部表就能够找到 节,我们的代码中的 代码与数据就放在这些节中,比如代码放在 .text 节、可读写变量放在 .data 节、初始化为0或者未初始化的全局或静态变量放在 .bss 节、变量名称放在 .strtab 节、节名称放在 .shstrtab 节等等。
根据分析 elf 头我们知道 section head table 的位置 0x1d0,我们找到 0x1d0 的位置,将该文件用 hexdump -C my_test.o 全部打印出来,如下(hexdump 自动省略了一部分为0数据,用 *代替)
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 d0 01 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 09 00 08 00 |....@.....@.....|
00000040 00 00 ff ff 11 11 ff ff 00 47 43 43 3a 20 28 55 |.........GCC: (U|
00000050 62 75 6e 74 75 20 37 2e 35 2e 30 2d 33 75 62 75 |buntu 7.5.0-3ubu|
00000060 6e 74 75 31 7e 31 38 2e 30 34 29 20 37 2e 35 2e |ntu1~18.04) 7.5.|
00000070 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 01 00 00 00 04 00 f1 ff 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 00 |................|
000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 03 00 02 00 00 00 00 00 00 00 00 00 |................|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 03 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 09 00 00 00 01 00 03 00 00 00 00 00 00 00 00 00 |................|
00000100 04 00 00 00 00 00 00 00 00 00 00 00 03 00 05 00 |................|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000120 00 00 00 00 03 00 04 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 12 00 00 00 11 00 02 00 |................|
00000140 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000150 19 00 00 00 11 00 02 00 04 00 00 00 00 00 00 00 |................|
00000160 04 00 00 00 00 00 00 00 00 68 65 6c 6c 6f 2e 63 |.........hello.c|
00000170 00 64 61 74 61 5f 62 73 73 00 64 61 74 61 5f 30 |.data_bss.data_0|
00000180 00 64 61 74 61 5f 31 00 00 2e 73 79 6d 74 61 62 |.data_1...symtab|
00000190 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
000001a0 61 62 00 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |ab..text..data..|
000001b0 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00 2e 6e 6f |bss..comment..no|
000001c0 74 65 2e 47 4e 55 2d 73 74 61 63 6b 00 00 00 00 |te.GNU-stack....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| // section head table
*
00000210 1b 00 00 00 01 00 00 00 06 00 00 00 00 00 00 00 |................|
00000220 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000240 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000250 21 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 |!...............|
00000260 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000270 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000280 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000290 27 00 00 00 08 00 00 00 03 00 00 00 00 00 00 00 |'...............|
000002a0 00 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00 |........H.......|
000002b0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000002d0 2c 00 00 00 01 00 00 00 30 00 00 00 00 00 00 00 |,.......0.......|
000002e0 00 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00 |........H.......|
000002f0 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |*...............|
00000300 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000310 35 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |5...............|
00000320 00 00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 |........r.......|
00000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000340 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000350 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................|
00000360 00 00 00 00 00 00 00 00 78 00 00 00 00 00 00 00 |........x.......|
00000370 f0 00 00 00 00 00 00 00 07 00 00 00 08 00 00 00 |................|
00000380 08 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 |................|
00000390 09 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|
000003a0 00 00 00 00 00 00 00 00 68 01 00 00 00 00 00 00 |........h.......|
000003b0 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
000003c0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000003d0 11 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|
000003e0 00 00 00 00 00 00 00 00 88 01 00 00 00 00 00 00 |................|
000003f0 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |E...............|
00000400 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000410
从 elf 头中可以知道每一个 section head table 的大小为 0x40 个字节,也就是从 0x1d0 开始,每 4 行数据代表一个 table,section head table 结构如下
typedef struct elf64_shdr {
Elf64_Word sh_name; /* 节名称在 节字符串表中的索引 */
Elf64_Word sh_type; /* 节类型 */
Elf64_Xword sh_flags; /* 标识该节的是否可修改可执行等内容 */
Elf64_Addr sh_addr; /* 该节在内存中的虚拟地址 */
Elf64_Off sh_offset; /* 节在该文件中的偏移 */
Elf64_Xword sh_size; /* 该节的大小 */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* 如果该节是一个表项,该值为每个表项的大小,如符号表 */
} Elf64_Shdr;
通过 struct elf64_shdr 这个结构,配合 hexdump 出来的数据,可以分析出各 section 的基本信息以及在该文件中的偏移,通过 readelf -h my_test.o 可以查看该 elf 文件中各个 section 的信息
可以发现,.text 节位于 section head table 的 1 号位置,而每个表项占用 sizeof(struct elf64_shdr) = 64 字节,因此 .text 节头部表在该文件的起始节头部表 + 64 字节 = 0x1d0 + 0x40 = 0x210 处,如下
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 d0 01 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 09 00 08 00 |....@.....@.....|
00000040 00 00 ff ff 11 11 ff ff 00 47 43 43 3a 20 28 55 |.........GCC: (U|
00000050 62 75 6e 74 75 20 37 2e 35 2e 30 2d 33 75 62 75 |buntu 7.5.0-3ubu|
00000060 6e 74 75 31 7e 31 38 2e 30 34 29 20 37 2e 35 2e |ntu1~18.04) 7.5.|
00000070 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 01 00 00 00 04 00 f1 ff 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 00 |................|
000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 03 00 02 00 00 00 00 00 00 00 00 00 |................|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 03 00 |................|
000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 09 00 00 00 01 00 03 00 00 00 00 00 00 00 00 00 |................|
00000100 04 00 00 00 00 00 00 00 00 00 00 00 03 00 05 00 |................|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000120 00 00 00 00 03 00 04 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 12 00 00 00 11 00 02 00 |................|
00000140 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000150 19 00 00 00 11 00 02 00 04 00 00 00 00 00 00 00 |................|
00000160 04 00 00 00 00 00 00 00 00 68 65 6c 6c 6f 2e 63 |.........hello.c|
00000170 00 64 61 74 61 5f 62 73 73 00 64 61 74 61 5f 30 |.data_bss.data_0|
00000180 00 64 61 74 61 5f 31 00 00 2e 73 79 6d 74 61 62 |.data_1...symtab|
00000190 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
000001a0 61 62 00 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |ab..text..data..| // .text: 2e 74 65 78 74
000001b0 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00 2e 6e 6f |bss..comment..no| // .data: 2e 64 61 74 61
000001c0 74 65 2e 47 4e 55 2d 73 74 61 63 6b 00 00 00 00 |te.GNU-stack....|
000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| // 0 号无名节的信息
*
00000210 1b 00 00 00 01 00 00 00 06 00 00 00 00 00 00 00 |................| // .text 节的信息
00000220 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000240 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000250 21 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 |!...............|// .data 节的信息
00000260 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000270 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000280 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000290 27 00 00 00 08 00 00 00 03 00 00 00 00 00 00 00 |'...............|// .bss 节的信息
000002a0 00 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00 |........H.......|
000002b0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000002d0 2c 00 00 00 01 00 00 00 30 00 00 00 00 00 00 00 |,.......0.......|// .comment 节信息
000002e0 00 00 00 00 00 00 00 00 48 00 00 00 00 00 00 00 |........H.......|
000002f0 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |*...............|
00000300 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 |................|
00000310 35 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |5...............|// .note.GNU-stack 节信息
00000320 00 00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 |........r.......|
00000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000340 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000350 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................|// .symtab 节信息
00000360 00 00 00 00 00 00 00 00 78 00 00 00 00 00 00 00 |........x.......|
00000370 f0 00 00 00 00 00 00 00 07 00 00 00 08 00 00 00 |................|
00000380 08 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 |................|
00000390 09 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|// .strtab 节信息
000003a0 00 00 00 00 00 00 00 00 68 01 00 00 00 00 00 00 |........h.......|
000003b0 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
000003c0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000003d0 11 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 |................|// .shstrtab 节信息
000003e0 00 00 00 00 00 00 00 00 88 01 00 00 00 00 00 00 |................|
000003f0 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |E...............|
00000400 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000410
分析 .text 节,该节的名称在 节字符串表中的偏移为 0x1b,而节字符串表在整个文件中的偏移为 0x188 处,因此,.text 节的节名 “.text” 在该 elf 文件的 0x188 + 0x1b = 0x1a3 处,以 \0 结尾,因此 0x1a3 处开始的 2e 74 65 78 74,这 5 个字节对应的 ascii 字符就是 .text,其余节同理,如 .data 节的名字在节字符串表中的偏移是 0x21,因此 0x21 + 0x188 = 0x1A9,对应的 ascii 码为 2e 64 61 74 61。
sh_entsize 这个成员很有意思,对于普通的节,该成员的值为 0,但有些节这个成员不为 0,表明这个节中的内容同样是表组成的,该表项的大小为 sh_entsize。
.symtab 节中 sh_entsize 为 18 00 00 00 00 00 00 00 也就是 0x18 字节,也就是 24 字节,表明该节中的内容同样是个表,类似于正在分析的 section head table (节头部表),为什么偏偏是 24 字节?通过符号表的结构来分析,如下,首先猜测,sizeof(struct elf64_sym) = 24 字节
typedef struct elf64_sym {
Elf64_Word st_name; /* 该符号的名字在字符串表中的起始下标 */
unsigned char st_info; /* Type and binding attributes */
unsigned char st_other; /* No defined meaning, 0 */
Elf64_Half st_shndx; /* Associated section index */
Elf64_Addr st_value; /* Value of the symbol */
Elf64_Xword st_size; /* Associated symbol size */
} Elf64_Sym;
对于符号表的分析,另起一篇文章,符号表还是蛮重要的。