Skip to content

Commit e998207

Browse files
committed
update elf part
1 parent fda4918 commit e998207

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

docs/executable/elf/elf_structure.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,10 @@ ELF 文件不仅可以导入外部的符号,而且还可以导入指定版本
612612

613613
#### 概述
614614

615+
每个目标文件都会有一个符号表,熟悉编译原理的就会知道,在编译程序时,必须有相应的结构来管理程序中的符号以便于对函数和变量进行重定位。
616+
617+
此外,链接本质就是把多个不同的目标文件相互“粘”在一起,实际上,目标文件相互粘合是目标文件之间对地址的引用,即函数和变量的地址的相互引用。而在粘合的过程中,符号就是其中的粘合剂。
618+
615619
目标文件中的符号表包含了**一些通用的符号**,这部分信息在进行了 `strip` 操作后就会消失。包括
616620

617621
- 变量名
@@ -639,7 +643,7 @@ typedef struct {
639643
| st_size | 给出对应符号所占用的大小。如果符号没有大小或者大小未知,则此成员为0。 |
640644
| st_info | 给出符号的类型和绑定属性。之后会给出若干取值和含义的绑定关系。 |
641645
| st_other | 目前为0,其含义没有被定义。 |
642-
| st_shndx | 每个符号表项都会和某个节区有所关系。此成员给出相关的节区头部表索引。某些索引具有特殊含义|
646+
| st_shndx | 如果符号定义在该文件中,那么该成员为符号所在节在节区头部表中的下标;如果符号不在本目标文件中国,或者对于某些特殊的符号,该成员具有一些特殊含义|
643647

644648
其中,符号表中下标 0 存储了符号表的一个元素,同时这个元素也相对比较特殊,作为所有未定义符号的索引,具体如下
645649

@@ -695,7 +699,7 @@ st_info 中包含符号类型和绑定信息,这里给出了控制它的值的
695699
| 名称 | 取值 | 说明 |
696700
| ----------------------- | ---- | ------------------------------------------------------------ |
697701
| STB_LOCAL | 0 | 表明该符号为局部符号,在包含该符号定义的目标文件以外不可见。相同名称的局部符号可以存在于多个文件中,互不影响。 |
698-
| STB_GLOBAL | 1 | 表明该符号为全局符号,对所有将被组合在一起的目标文件都是可见的。一个文件中对某个全局符号的定义将满足另一个文件对相同全局符号的未定义引用。 |
702+
| STB_GLOBAL | 1 | 表明该符号为全局符号,对所有将被组合在一起的目标文件都是可见的。一个文件中对某个全局符号的定义将满足另一个文件对相同全局符号的未定义引用。我们称初始化非零变量的全局符号为强符号,只能定义一次。 |
699703
| STB_WEAK | 2 | 弱符号与全局符号类似,不过它们的定义优先级比较低。 |
700704
| STB_LOPROC ~STB_HIPROC | 13 | 这个范围的取值是保留给处理器专用语义的。 |
701705

@@ -917,9 +921,11 @@ typedef struct
917921
在这样的情况下,动态链接器使用 Elf_Rel 结构体成员 r_info 中的下标同时作为 .dynsym 段和 gnu.version 段的下标。这样就可以一一对应到每一个符号到底是那个版本的了。
918922

919923

920-
### Relocation Sections
924+
### Relocation Related Sections
925+
926+
链接器在处理目标文件时,需要对目标文件中的某些位置进行重定位,即将符号指向恰当的位置,确保程序正常执行。例如,当程序调用了一个函数时,相关的调用指令必须把控制流交给适当的目标执行地址。
921927

922-
重定位其实就是将符号指向恰当的位置,确保程序正常执行。例如,当程序调用了一个函数时,相关的调用指令必须把控制流交给适当的目标执行地址
928+
在 ELF 文件中,对于每一个需要重定位的 ELF 节都有对应的重定位表,比如说 .text 节如果需要重定位,那么其对应的重定位表为 .rel.text
923929

924930
举个例子,当一个程序导入某个函数时,.dynstr 就会包含对应函数名称的字符串,.dynsym 中就会包含一个具有相应名称的动态字符串表的符号(Elf_Sym),在 rel.dyn 中就会包含一个指向这个符号的的重定位表项。
925931

@@ -1046,6 +1052,8 @@ GOT 表用来将位置独立的地址重定向为绝对地址,与此类似,P
10461052
- **.plt**,与常见导入的函数有关,如 read 等函数。
10471053
- **.plt.got**,与动态链接有关系。
10481054

1055+
在动态链接下,程序模块之间包含了大量的函数引用,程序开始执行前,动态链接会耗费不少时间用于解决模块之间的函数引用的符号查找以及重定位。但是,在一个程序运行过程中,可能很多函数在程序执行完时都不会用到,因此一开始就把所有函数都链接好是一种浪费,所以 ELF 采用了一种延迟绑定的做法,其基本思想是函数第一次被用到时才进行绑定(符号查找,重定位等),如果没有用则不进行绑定。所以程序开始执行前,模块间的函数调用都没有进行绑定,而是需要用到时才由动态链接器负责绑定。
1056+
10491057
链接编辑器不能够解析执行流转换(比如程序调用),即从一个可执行文件或者共享目标文件到另一个文件。链接器安排程序将控制权交给过程链接表中的表项。在 Intel 架构中,过程链接表存在于共享代码段中,但是他们会使用在 GOT 表中的数据。动态链接器会决定目标的绝对地址,并且会修改相应的 GOT 表中的内存镜像。因此,动态链接器可以在不违背位置独立以及程序代码段兼容的情况下,重定向 PLT 项。可执行文件和共享目标文件都有独立的 PLT 表。
10501058

10511059
绝对地址的过程链接表如下
@@ -1093,6 +1101,10 @@ GOT 表用来将位置独立的地址重定向为绝对地址,与此类似,P
10931101
7. 当动态链接器接收到控制权后,他将会进行出栈操作,查看重定位表项,找到对应的符号的值,将 name1 的地址存储在全局偏移表项中,然后将控制权交给目的地址。
10941102
8. 过程链接表执行之后,程序的控制权将会直接交给 name1 函数,而且此后再也不会调用动态链接器来解析这个函数。也就是说,在 .PLT1 处的 jmp 指令将会直接跳转到 name1 处,而不是再次执行 pushl 指令。
10951103

1104+
总体流程如下图所示,蓝线表示首次执行的流程图,红线表示第二次以后调用的流程图:
1105+
1106+
![](/executable/elf/figure/lazy-plt.png)
1107+
10961108
LD_BIND_NOW 环境变量可以改变动态链接器的行为。如果它的值非空的话,动态链接器在将控制权交给程序之前会执行 PLT 表项。也就是说,动态链接器在进程初始化过程中执行类型为 R\_3862\_JMP_SLOT 的重定位表项。否则的话,动态链接表会对过程链接表项进行延迟绑定,直到第一次执行对应的表项时,才会今次那个符号解析以及重定位。
10971109

10981110
注意
@@ -1136,4 +1148,5 @@ _dl_runtime_resolve(link_map_obj, reloc_index)
11361148
11371149
## 参考文献
11381150
1139-
- https://blogs.oracle.com/ali/gnu-hash-elf-sections
1151+
- https://blogs.oracle.com/ali/gnu-hash-elf-sections
1152+
- https://bbs.pediy.com/thread-204642.htm
75.3 KB
Loading

0 commit comments

Comments
 (0)