ELF格式学习
一、简介
ELF的英文全称是Executable and Linking Format,最初是由UNIX系统实验室开发、发布的ABI(Application Binary Interface)接口的一部分,也是Linux的主要可执行文件格式。
ELF文件种类:
- 可执行文件(.out):Executable File,包含代码和数据,是可以直接运行的程序。其代码和数据都有固定的地址 (或相对于基地址的偏移 ),系统可根据这些地址信息把程序加载到内存执行。
- 可重定位文件(.o文件):Relocatable File,包含基础代码和数据,但它的代码及数据都没有指定绝对地址,因此它适合于与其他目标文件链接来创建可执行文件或者共享目标文件。
- 共享目标文件(.so):Shared Object File,也称动态库文件,包含了代码和数据,这些数据是在链接时被链接器(ld)和运行时动态链接器(ld.so.l、libc.so.l、ld-linux.so.l)使用的。
二、文件结构
- ELF header: 描述整个文件的组织。
- Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。
- sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出,segments与sections是包含的关系,一个segment包含若干个section。
- Section Header Table:包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
三、数据结构
ELF相关的数据结构在linux系统下有定义,路径在/usr/include/elf.h里面。
Elf32_Ehdr是32位 ELF header的结构体。Elf64_Ehdr是64位ELF header的结构体。
ELF头格式(ELF header)
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* 文件类型 */
Elf64_Half e_machine; /* 机器类型 */
Elf64_Word e_version; /* 文件版本 */
Elf64_Addr e_entry; /* 进程开始的虚拟地址 */
Elf64_Off e_phoff; /* 指向程序头部表的开始 */
Elf64_Off e_shoff; /* 指向节头部表的开始 */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
e_type的取值:
0 | ET_NONE | No file type |
---|---|---|
1 | ET_REL | 重定位文件 |
2 | ET_EXEC | 可执行文件 |
3 | ET_DYN | 共享库文件 |
4 | ET_CORE | 核心转储文件 |
5 | ET_NUM | 表示已经前面定义了5种文件类型 |
0xfe00 | ET_LOOS | OS-specific range start |
0xfeff | ET_HIOS | OS-specific range end |
0xff00 | ET_LOPROC | Processor-specific range start |
0xffff | ET_HIPROC | Processor-specific range end |
ELF文件格式的最前部是ELF文件头,它描述整个文件的基本属性,比如ELF文件版本,目标机器型号,程序的入口地址。
可以使用readelf -h命令来读取ELF头信息
:
root@ubuntu:/media/code_test# readelf -h hello.o
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: 976 (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: 14
Section header string table index: 13
程序头格式(program header)
typedef struct
{
Elf64_Word p_type; /* 当前Program header所描述的段的类型 */
Elf64_Word p_flags; /* 与段相关的标志 */
Elf64_Off p_offset; /* 段的第一个字节在文件中的偏移 */
Elf64_Addr p_vaddr; /* 段的第一个字节在内存中的虚拟地址 */
Elf64_Addr p_paddr; /* 在物理内存定位相关的系统中,此项是为物理地址保留 */
Elf64_Xword p_filesz; /* 段在文件中的长度 */
Elf64_Xword p_memsz; /* 段在内存中的长度 */
Elf64_Xword p_align; /* 根据此项值来确定段在文件及内存中如何对齐 */
} Elf64_Phdr;
p_type取值:
0 | PT_NULL | 无意义,可忽略 |
---|---|---|
1 | PT_LOAD | 可加载段。段数据由文件映射到内存,如果 p_memsz 大于 p_filesz,则额外部分填充为 0 |
2 | PT_DYNAMIC | 动态段。包含动态链接所需的信息 |
3 | PT_INTERP | 本段包含一个路径字符串,该路径存放解释器 |
4 | PT_NOTE | 注释段,包含一些辅助信息 |
5 | PT_SHLIB | 保留的段类型,暂不关心 |
6 | PT_PHDR | 程序头段。指明程序头表在文件和内存映像中的位置和大小 |
7 | PT_TLS | 线程本地存储段 |
8 | PT_NUM | 表示已经前面定义了8种段类型 |
程序头定义了可执行文件在装入内存后的段内存布局,每个程序头对应进程的一个内存段,程序头对于目标文件不是必须的,而且用gcc编译的.o文件没有程序头。
可以使用readelf -l命令来读取程序头信息
:
root@ubuntu:/media/code_test# readelf -lW hello.o
There are no program headers in this file.
root@ubuntu:/media/code_test# readelf -lW hello
Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R 0x8
INTERP 0x000318 0x0000000000000318 0x0000000000000318 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0005f8 0x0005f8 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0001f5 0x0001f5 R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000160 0x000160 R 0x1000
LOAD 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000264 0x000278 RW 0x1000
DYNAMIC 0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW 0x8
NOTE 0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R 0x8
NOTE 0x000358 0x0000000000000358 0x0000000000000358 0x000044 0x000044 R 0x4
GNU_PROPERTY 0x000338 0x0000000000000338 0x0000000000000338 0x000020 0x000020 R 0x8
GNU_EH_FRAME 0x002014 0x0000000000002014 0x0000000000002014 0x000044 0x000044 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R 0x1
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
节头格式(section header)
typedef struct
{
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; /* 指定链接的节索引,与具体的节有关 */
Elf64_Word sh_info; /* 指定附加信息 */
Elf64_Xword sh_addralign; /* 节装入内存的地址对齐要求 */
Elf64_Xword sh_entsize; /* 指定某些节的固定表大小,与具体的节有关 */
} Elf64_Shdr;
sh_type取值:
0 | SHT_NULL | 无对应节区,该节其他字段取值无意义 |
---|---|---|
1 | SHT_PROGBITS | 程序数据 |
2 | SHT_SYMTAB | 符号表 |
3 | SHT_STRTAB | 字符串表 |
4 | SHT_RELA | 带附加的重定位项 |
5 | SHT_HASH | 符号哈希表 |
6 | SHT_DYNAMIC | 动态链接信息 |
7 | SHT_NOTE | 提示性信息 |
8 | SHT_NOBITS | 无数据程序空间(bss) |
9 | SHT_REL | 无附加的重定位项 |
10 | SHT_SHLIB | 保留 |
11 | SHT_DYNSYM | 动态链接符号表 |
14 | SHT_INIT_ARRAY | 构造函数数组 |
15 | SHT_FINI_ARRAY | 析构函数数组 |
节头定义了目标文件的代码及数据在文件中的位置
可以使用readelf -S命令来读取节头信息
:
root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000040 000022 00 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 000310 000030 18 I 11 1 8
[ 3] .data PROGBITS 0000000000000000 000064 00000c 00 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000070 000008 00 WA 0 0 4
[ 5] .rodata PROGBITS 0000000000000000 000070 00000d 00 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 00007d 00002c 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 0000a9 000000 00 0 0 1
[ 8] .note.gnu.property NOTE 0000000000000000 0000b0 000020 00 A 0 0 8
[ 9] .eh_frame PROGBITS 0000000000000000 0000d0 000038 00 A 0 0 8
[10] .rela.eh_frame RELA 0000000000000000 000340 000018 18 I 11 9 8
[11] .symtab SYMTAB 0000000000000000 000108 0001c8 18 12 14 8
[12] .strtab STRTAB 0000000000000000 0002d0 00003d 00 0 0 1
[13] .shstrtab STRTAB 0000000000000000 000358 000074 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
这里有个概念需要解释清楚,就是节(section)和段(segment)。段是程序执行的必要组成部分,在每个段中,会有代码或者数据被划分为不同的节。每一个节都保存了某种类型的代码或者是数据,数据可以是程序中的全局变量,也可以是链接器所需要的动态链接信息。segment是从运行的角度来描述elf文件,section是从链接的角度来描述elf文件。
常见的节:
.text | 保存了程序代码指令 |
---|---|
.rodata | 保存只读数据,如c语言代码中的字符串“Hello World”。因为它是只读的,所以只能存在于一个可执行文件的只读段中(text段)。 |
.plt | .plt节包含了动态链接器调用从共享库导入的函数所需要的相关代码,也是存在于text段中。 |
.data | 这里不要将.data节和data段混淆了,.data节存在于data段中,保存了初始化的全局变量数据。 |
.bss | .bss节保存了未初始化的全局数据,是data段的一部分,占用空间不超过4字节(使用占位符),程序加载时数据被初始化为0。 |
.got.plt | .got节保存了全局偏移表。.got节和.plt节一起提供了对导入的共享库函数的访问入口。 |
.dynsym | .dynsym节保存了从共享库导入的动态符号信息,该节保存在text段中。 |
.dynstr | .dynstr节保存了动态符号字符串表,表中存放了一系列字符串。 |
.rel.* | .rel.*节又叫重定位节,保存了重定位相关信息,这些信息描述了如何在链接或者运行时,对ELF目标文件的某部分内容或者进程镜像进行补充或修改。 |
.hash | .hash节有时又称.gnu.hash,保存了一个用于查找符号的散列表。 |
.symtab | .symtab节保存了Elf64_Sym或者Elf32_Sym类型的符号信息。 |
.strtab | .strtab节保存的是符号字符串表,表中的内容会被.symtab的Elfx_Sym结构体中的st_name成员引用。 |
.shstrtab | .shstrtab节保存节头字符串表。 |
从 ELF Header 中可以看出 Section header 的 offset 是976字节,每个 Section Header 大小为64 bytes,一共有14个。
root@ubuntu:/media/code_test# readelf -h hello.o
ELF Header:
...
Start of section headers: 976 (bytes into file)
...
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13
Section Header undefined
root@ubuntu:/media/code_test# hexdump -C -s976 -n64 hello.o
000003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000410
可以看到 index=0 的 Section Header,内容全部为0。(在某些情况下,它的某部分字段不为0,这些特殊情况当前先跳过)
这是一个非常特别的 Section Header,它的作用是表示 undefined 。它的 index(=0) 也有一个特别的名字,叫 SHN_UNDEF.
Section Header .text
root@ubuntu:/media/code_test# hexdump -C -s1040 -n64 hello.o
00000410 20 00 00 00 01 00 00 00 06 00 00 00 00 00 00 00 | ...............|
00000420 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000430 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |"...............|
00000440 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000450
这里dump出了.text的section header内容,总共64字节,对照Elf64_Shdr结构体分析即可。后续的section header也是类似的方法来分析。
ELF符号
来看一下一个64位ELF文件符号项的结构:
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index):4 Bytes */
unsigned char st_info; /* Symbol type and binding:1 Byte */
unsigned char st_other; /* Symbol visibility:1 Byte */
Elf64_Section st_shndx; /* Section indexL:2 Bytes */
Elf64_Addr st_value; /* Symbol value:8 Bytes */
Elf64_Xword st_size; /* Symbol size:8 Bytes*/
} Elf64_Sym; //total size = 24 Bytes
符号项保存在.symtab和.dynsym节中,因此它们对应的section header大小与Elfx_Sym的大小相等。
用 readelf 查看一下 Symbol Table:(这里小写的 s,代表 Symbols,大写的 S,代表 Sections/Section Headers)
root@ubuntu:/media/code_test# readelf -sW hello.o
Symbol table '.symtab' contains 19 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 c
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 d
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 h.2323
9: 0000000000000008 4 OBJECT LOCAL DEFAULT 3 g.2322
10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
11: 0000000000000000 0 SECTION LOCAL DEFAULT 8
12: 0000000000000000 0 SECTION LOCAL DEFAULT 9
13: 0000000000000000 0 SECTION LOCAL DEFAULT 6
14: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 a
15: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM b
16: 0000000000000000 34 FUNC GLOBAL DEFAULT 1 main
17: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
这里包含了hello.o中出现的所有 Symbol。其中 Num=0(index=0) 的 Symbol 用作 undefined Symbol,它的 index 有个特别的名字,叫做 STN_UNDEF。
四、符号表& 符号
Symbol Table 包含了一组 Symbol。这些 Symbol 在程序中,要么表示定义,要么表示引用,它们的作用是在编译和链接的过程中,进行定位或者重定位。
先查看它在 Section Header 列表中的信息:
root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[11] .symtab SYMTAB 0000000000000000 000108 0001c8 18 12 14 8
...
由上可知 Symbol Table Section:
- sh_name是 “.symtab”
- sh_type是 SYMTAB
- sh_offset = 0x108 = 264 Bytes
- sh_size = 0x1c8 = 456 Bytes
- sh_entsize = 0x18 = 24 Bytes
- Symbol 数为: 456 / 24 = 19
我们尝试从字节级别解读 Symbol 的定义。先获取Symbol信息:
root@ubuntu:/media/code_test# readelf -sW hello.o
Symbol table '.symtab' contains 19 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000004 4 OBJECT LOCAL DEFAULT 3 c
6: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 d
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 h.2323
9: 0000000000000008 4 OBJECT LOCAL DEFAULT 3 g.2322
10: 0000000000000000 0 SECTION LOCAL DEFAULT 7
11: 0000000000000000 0 SECTION LOCAL DEFAULT 8
12: 0000000000000000 0 SECTION LOCAL DEFAULT 9
13: 0000000000000000 0 SECTION LOCAL DEFAULT 6
14: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 a
15: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM b
16: 0000000000000000 34 FUNC GLOBAL DEFAULT 1 main
17: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
18: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
选取Num = 6,也就是Name = d 的 Symbol。
上面已近计算出了Symbol Table 在对象文件中的 offset = 264,每个 Symbol 的 size = 24,那么 Num = 6 的 Symbol 它的 offset = 264 + 24 * 6 = 408。
root@ubuntu:/media/code_test# hexdump -C -s408 -n24 hello.o
00000198 09 00 00 00 01 00 04 00 00 00 00 00 00 00 00 00 |................|
000001a8 04 00 00 00 00 00 00 00 |........|
000001b0
st_name = 0x00000009 (4 Bytes)
Symbol 名字的索引,根据它去.strtab里寻找字符串
root@ubuntu:/media/code_test# readelf -SW hello.o
There are 14 section headers, starting at offset 0x3d0:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
...
[12] .strtab STRTAB 0000000000000000 0002d0 00003d 00 0 0 1
...
可以看到,.strtab的偏移是0x2d0
root@ubuntu:/media/code_test# hexdump -C -s720 hello.o
000002d0 00 68 65 6c 6c 6f 2e 63 00 64 00 68 2e 32 33 32 |.hello.c.d.h.232|
000002e0 33 00 67 2e 32 33 32 32 00 61 00 62 00 6d 61 69 |3.g.2322.a.b.mai|
000002f0 6e 00 5f 47 4c 4f 42 41 4c 5f 4f 46 46 53 45 54 |n._GLOBAL_OFFSET|
00000300 5f 54 41 42 4c 45 5f 00 70 75 74 73 00 00 00 00 |_TABLE_.puts....|
00000310 16 00 00 00 00 00 00 00 02 00 00 00 07 00 00 00 |................|
00000320 fc ff ff ff ff ff ff ff 1b 00 00 00 00 00 00 00 |................|
00000330 04 00 00 00 12 00 00 00 fc ff ff ff ff ff ff ff |................|
00000340 20 00 00 00 00 00 00 00 02 00 00 00 02 00 00 00 | ...............|
00000350 00 00 00 00 00 00 00 00 00 2e 73 79 6d 74 61 62 |..........symtab|
00000360 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
00000370 61 62 00 2e 72 65 6c 61 2e 74 65 78 74 00 2e 64 |ab..rela.text..d|
00000380 61 74 61 00 2e 62 73 73 00 2e 72 6f 64 61 74 61 |ata..bss..rodata|
00000390 00 2e 63 6f 6d 6d 65 6e 74 00 2e 6e 6f 74 65 2e |..comment..note.|
000003a0 47 4e 55 2d 73 74 61 63 6b 00 2e 6e 6f 74 65 2e |GNU-stack..note.|
000003b0 67 6e 75 2e 70 72 6f 70 65 72 74 79 00 2e 72 65 |gnu.property..re|
000003c0 6c 61 2e 65 68 5f 66 72 61 6d 65 00 00 00 00 00 |la.eh_frame.....|
000003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
...
索引值是9,所以数9个字节,直到遇到00为止(ASCII码值 0 表示空字符,空字符就是平时所说的 ‘\0’。)
64 —> d
00 —> \0
这里得到的结果就是字符d,和上面Symbol table的Name结果是一致的。
st_info = 0x01 (1 Byte)
这是个组合字段。它的高4位表示 Symbol Binding,低4位表示 Symbol Type。
0x01 = 0b0000 0001
Symbol Binding = 0
0 | STB_LOCAL | 局部符号。这些符号在包含其定义的目标文件的外部不可见。名称相同的局部符号可存在于多个文件中而不会相互干扰。 |
---|---|---|
1 | STB_GLOBAL | 全局符号。这些符号对于合并的所有目标文件都可见。一个文件的全局符号定义满足另一个文件对相同全局符号的未定义引用。 |
2 | STB_WEAK | 弱符号。这些符号与全局符号类似,但其定义具有较低的优先级。 |
10 | STB_LOOS | 此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。 |
12 | STB_HIOS | 此范围内包含的值(包括这两个值)保留用于特定于操作系统的语义。 |
13 | STB_LOPROC | 此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。 |
15 | STB_HIPROC | 此范围内包含的值(包括这两个值)保留用于特定于处理器的语义。 |
Symbol Type = 1
0 | STT_NOTYPE | 未指定符号类型。 |
---|---|---|
1 | STT_OBJECT | 此符号与变量、数组等数据目标文件关联 |
2 | STT_FUNC | 此符号与函数或其他可执行代码关联。 |
3 | STT_SECTION | 此符号与节关联。此类型的符号表各项主要用于重定位,并且通常具有 STB_LOCAL 绑定。 |
4 | STT_FILE | 通常,符号的名称会指定与目标文件关联的源文件的名称。文件符号具有 STB_LOCAL 绑定和节索引 SHN_ABS 。此符号(如果存在)位于文件的其他 STB_LOCAL 符号前面。符号索引为 1 的 SHT_SYMTAB 是表示目标文件的 STT_FILE 符号。通常,此符号后跟文件的 STT_SECTION 符号。这些节符号又后跟已降为局部符号的任何全局符号。 |
5 | STT_COMMON | 此符号标记未初始化的通用块。此符号的处理与 STT_OBJECT 的处理完全相同。 |
6 | STT_TLS | 此符号指定线程局部存储实体。定义后,此符号可为符号指明指定的偏移,而不是实际地址。线程局部存储重定位只能引用 STT_TLS 类型的符号。从可分配节中引用 STT_TLS 类型的符号只能通过使用特殊线程局部存储重定位来实现。 |
st_other = 0x00 (1 Byte)
指定了 Symbol 的可见性
0 | STV_DEFAULT | 具有 STV_DEFAULT 属性的符号的可见性与符号的绑定类型指定的可见性相同。全局符号和弱符号在其定义组件(可执行文件或共享目标文件)外部可见。局部符号处于隐藏状态。另外,还可以替换全局符号和弱符号。可以在另一个组件中通过定义相同的名称插入这些符号 |
---|---|---|
1 | STV_INTERNAL | 此可见性属性当前被保留。 |
2 | STV_HIDDEN | 如果当前组件中定义的符号的名称对于其他组件不可见,则该符号处于隐藏状态。必须对这类符号进行保护。此属性用于控制组件的外部接口。由这样的符号命名的目标文件仍可以在另一个组件中引用(如果将目标文件的地址传到外部)。如果可执行文件或共享目标文件中包括可重定位目标文件,则该目标文件中包含的隐藏符号将会删除或转换为使用 STB_LOCAL 绑定。 |
3 | STV_PROTECTED | 如果当前组件中定义的符号在其他组件中可见,但不能被替换,则该符号处于受保护状态。定义组件中对这类符号的任何引用都必须解析为该组件中的定义。即使在另一个组件中存在按缺省规则插入的符号定义,也必须进行此解析。具有 STB_LOCAL 绑定的符号将没有 STV_PROTECTED 可见性。 |
4 | STV_EXPORTED | 此可见性属性确保符号保持为全局。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_EXPORTED 可见性。 |
5 | STV_SINGLETON | 此可见性属性确保符号保持为全局。不能使用任何其他符号可见性技术对此可见性进行降级或消除。具有 STB_LOCAL 绑定的符号将没有 STV_EXPORTED 可见性。 |
6 | STV_ELIMINATE | 此可见性属性扩展 STV_HIDDEN 。当前组件中定义为要消除的符号对其他组件不可见。该符号未写入使用该组件的动态可执行文件或共享目标文件的任何符号表中。 |
st_shndx = 0x0004 (2 Bytes)
关联到section header table,这里的值为4,对应[ 4] .bss
st_value = 0x0000000000000000 (8 Bytes)
对于 relocatable (.o) 文件,如果该符号已定义,则 st_value 保存的是该符号在其所定义的 section (由 st_shndx 指定) 中的偏移量。
st_size = 0x0000000000000004 (8 Bytes)
表示 Symbol 的大小,例如数据对象占据多少字节。如果 Symbol 没有大小或者大小未知,则值为0。
总结一下,一个symbol起始和结束如下:
Start:
EL64_Shdr[Elf64_Sym.st_section].sh_offset + El64_Sym.st_value
End:
Start + Elf64_Sym.size -1
五、参考文档
1.《Oracle Solaris 11 Information Library 》