前言

本文转载自知乎,仅用作存储用。

再看动态链接中的PLT和GOT - 读懂原理与细节

作者主页:红色的红

正文

原背景

原背景
  • 魔鬼都藏在细节中, 不深入细节我总以为我已经懂了. 此文就是用于解决所有细节的落脚。

待解决的疑问:

  • 动态库中的PLT表与主程序中的PLT表是否为同一份。
  • 动态库中的GOT.PLT表与主程序中的GOT.PLT表是否为同一份。
  • PLT表是代码还是数据。
  • 动态库内部函数之间的调用为何不直接使用相对地址调用。

  • 由于.plt段的作用(用于延时加载)不是 这篇文章的重点. 我们都知道其延时加载的原理和基本方法.这里就不额外展开说明这一点.可以参考内容[1][2].

代码准备

  • 简单对代码作一个说明:
  • add.so 会被编译为一个动态库. 里面包含两个函数:
    • addtwo 完成两个数的加法.
    • addthree 完成

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

// 声明一个外部导入的函数
int addtwo(int x,int y);
int addthree(int x,int y,int z);
int main(int argc, char* argv[])
{
int sum = addtwo(1,2);
printf("1+2=%d\n",sum);
sum = addthree(1,2,3);
printf("1+2+3=%d\n",sum);
return 0;
}

动态库: add.so

源文件: add.c

1
2
3
4
5
6
7
8
9
10
11
12
13
int addtwo(int x,int y)
{
int sum = 0;
sum = x + y;
return sum;
}

int addthree(int x,int y,int z)
{
int sum = 0;
sum = addtwo(x,y)+z;
return sum;
}

makefile

一份”能用”的makefile,主要是用于记录编译参数. 方便重复输入:

1
2
3
4
so:
gcc -m32 -g -Wl,-soname,add.so -shared -fPIC -o add.so add.c
main:
gcc -m32 -g -Wl,-rpath,./ -o main.out main.c add.so

编译结果分析

  • 我们着重对相关的.dynamic和相关的符号(symbol)和重定位表rel.dyn等进行详细分析。

分析: add.so

  • 命令: objdump -d add.so
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
 .add.so:     file format elf32-i386


Disassembly of section .init:

000002dc <_init>:
2dc: 55 push %ebp
2dd: 89 e5 mov %esp,%ebp
2df: 53 push %ebx
2e0: 83 ec 04 sub $0x4,%esp
2e3: e8 00 00 00 00 call 2e8 <_init+0xc>
2e8: 5b pop %ebx
2e9: 81 c3 cc 12 00 00 add $0x12cc,%ebx
2ef: 8b 93 f4 ff ff ff mov -0xc(%ebx),%edx
2f5: 85 d2 test %edx,%edx
2f7: 74 05 je 2fe <_init+0x22>
2f9: e8 1e 00 00 00 call 31c <__gmon_start__@plt>
2fe: e8 cd 00 00 00 call 3d0 <frame_dummy>
303: e8 68 01 00 00 call 470 <__do_global_ctors_aux>
308: 58 pop %eax
309: 5b pop %ebx
30a: c9 leave
30b: c3 ret

; =====================================================
; add.so 动态链接库的 .plt 段 .
; =====================================================
Disassembly of section .plt:

0000030c <__gmon_start__@plt-0x10>:
30c: ff b3 04 00 00 00 pushl 0x4(%ebx)
312: ff a3 08 00 00 00 jmp *0x8(%ebx)
318: 00 00 add %al,(%eax)
...

0000031c <__gmon_start__@plt>:
31c: ff a3 0c 00 00 00 jmp *0xc(%ebx)
322: 68 00 00 00 00 push $0x0
327: e9 e0 ff ff ff jmp 30c <_init+0x30>

0000032c <addtwo@plt>:
32c: ff a3 10 00 00 00 jmp *0x10(%ebx)
332: 68 08 00 00 00 push $0x8
337: e9 d0 ff ff ff jmp 30c <_init+0x30>

0000033c <__cxa_finalize@plt>:
33c: ff a3 14 00 00 00 jmp *0x14(%ebx)
342: 68 10 00 00 00 push $0x10
347: e9 c0 ff ff ff jmp 30c <_init+0x30>

Disassembly of section .text:

00000350 <__do_global_dtors_aux>:
350: 55 push %ebp
351: 89 e5 mov %esp,%ebp
353: 56 push %esi
354: 53 push %ebx
355: e8 ad 00 00 00 call 407 <__i686.get_pc_thunk.bx>
35a: 81 c3 5a 12 00 00 add $0x125a,%ebx
360: 83 ec 10 sub $0x10,%esp
363: 80 bb 18 00 00 00 00 cmpb $0x0,0x18(%ebx)
36a: 75 5d jne 3c9 <__do_global_dtors_aux+0x79>
36c: 8b 83 fc ff ff ff mov -0x4(%ebx),%eax
372: 85 c0 test %eax,%eax
374: 74 0e je 384 <__do_global_dtors_aux+0x34>
376: 8d 83 28 ff ff ff lea -0xd8(%ebx),%eax
37c: 89 04 24 mov %eax,(%esp)
37f: e8 b8 ff ff ff call 33c <__cxa_finalize@plt>
384: 8b 83 1c 00 00 00 mov 0x1c(%ebx),%eax
38a: 8d b3 20 ff ff ff lea -0xe0(%ebx),%esi
390: 8d 93 1c ff ff ff lea -0xe4(%ebx),%edx
396: 29 d6 sub %edx,%esi
398: c1 fe 02 sar $0x2,%esi
39b: 83 ee 01 sub $0x1,%esi
39e: 39 f0 cmp %esi,%eax
3a0: 73 20 jae 3c2 <__do_global_dtors_aux+0x72>
3a2: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
3a8: 83 c0 01 add $0x1,%eax
3ab: 89 83 1c 00 00 00 mov %eax,0x1c(%ebx)
3b1: ff 94 83 1c ff ff ff call *-0xe4(%ebx,%eax,4)
3b8: 8b 83 1c 00 00 00 mov 0x1c(%ebx),%eax
3be: 39 f0 cmp %esi,%eax
3c0: 72 e6 jb 3a8 <__do_global_dtors_aux+0x58>
3c2: c6 83 18 00 00 00 01 movb $0x1,0x18(%ebx)
3c9: 83 c4 10 add $0x10,%esp
3cc: 5b pop %ebx
3cd: 5e pop %esi
3ce: 5d pop %ebp
3cf: c3 ret

000003d0 <frame_dummy>:
3d0: 55 push %ebp
3d1: 89 e5 mov %esp,%ebp
3d3: 53 push %ebx
3d4: e8 2e 00 00 00 call 407 <__i686.get_pc_thunk.bx>
3d9: 81 c3 db 11 00 00 add $0x11db,%ebx
3df: 83 ec 14 sub $0x14,%esp
3e2: 8b 93 24 ff ff ff mov -0xdc(%ebx),%edx
3e8: 85 d2 test %edx,%edx
3ea: 74 15 je 401 <frame_dummy+0x31>
3ec: 8b 83 f8 ff ff ff mov -0x8(%ebx),%eax
3f2: 85 c0 test %eax,%eax
3f4: 74 0b je 401 <frame_dummy+0x31>
3f6: 8d 93 24 ff ff ff lea -0xdc(%ebx),%edx
3fc: 89 14 24 mov %edx,(%esp)
3ff: ff d0 call *%eax
401: 83 c4 14 add $0x14,%esp
404: 5b pop %ebx
405: 5d pop %ebp
406: c3 ret

00000407 <__i686.get_pc_thunk.bx>:
407: 8b 1c 24 mov (%esp),%ebx
40a: c3 ret
40b: 90 nop

0000040c <addtwo>:
40c: 55 push %ebp
40d: 89 e5 mov %esp,%ebp
40f: 83 ec 10 sub $0x10,%esp
412: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
419: 8b 45 0c mov 0xc(%ebp),%eax
41c: 8b 55 08 mov 0x8(%ebp),%edx
41f: 8d 04 02 lea (%edx,%eax,1),%eax
422: 89 45 fc mov %eax,-0x4(%ebp)
425: 8b 45 fc mov -0x4(%ebp),%eax
428: c9 leave
429: c3 ret

0000042a <addthree>:
42a: 55 push %ebp
42b: 89 e5 mov %esp,%ebp
42d: 53 push %ebx
42e: 83 ec 24 sub $0x24,%esp
431: e8 d1 ff ff ff call 407 <__i686.get_pc_thunk.bx>
436: 81 c3 7e 11 00 00 add $0x117e,%ebx
43c: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
443: 8b 45 0c mov 0xc(%ebp),%eax
446: 89 44 24 04 mov %eax,0x4(%esp)
44a: 8b 45 08 mov 0x8(%ebp),%eax
44d: 89 04 24 mov %eax,(%esp)
450: e8 d7 fe ff ff call 32c <addtwo@plt>
455: 03 45 10 add 0x10(%ebp),%eax
458: 89 45 f4 mov %eax,-0xc(%ebp)
45b: 8b 45 f4 mov -0xc(%ebp),%eax
45e: 83 c4 24 add $0x24,%esp
461: 5b pop %ebx
462: 5d pop %ebp
463: c3 ret
464: 90 nop
465: 90 nop
466: 90 nop
467: 90 nop
468: 90 nop
469: 90 nop
46a: 90 nop
46b: 90 nop
46c: 90 nop
46d: 90 nop
46e: 90 nop
46f: 90 nop

00000470 <__do_global_ctors_aux>:
470: 55 push %ebp
471: 89 e5 mov %esp,%ebp
473: 56 push %esi
474: 53 push %ebx
475: e8 8d ff ff ff call 407 <__i686.get_pc_thunk.bx>
47a: 81 c3 3a 11 00 00 add $0x113a,%ebx
480: 8b 83 14 ff ff ff mov -0xec(%ebx),%eax
486: 83 f8 ff cmp $0xffffffff,%eax
489: 74 19 je 4a4 <__do_global_ctors_aux+0x34>
48b: 8d b3 14 ff ff ff lea -0xec(%ebx),%esi
491: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
498: 83 ee 04 sub $0x4,%esi
49b: ff d0 call *%eax
49d: 8b 06 mov (%esi),%eax
49f: 83 f8 ff cmp $0xffffffff,%eax
4a2: 75 f4 jne 498 <__do_global_ctors_aux+0x28>
4a4: 5b pop %ebx
4a5: 5e pop %esi
4a6: 5d pop %ebp
4a7: c3 ret

Disassembly of section .fini:

000004a8 <_fini>:
4a8: 55 push %ebp
4a9: 89 e5 mov %esp,%ebp
4ab: 53 push %ebx
4ac: 83 ec 04 sub $0x4,%esp
4af: e8 00 00 00 00 call 4b4 <_fini+0xc>
4b4: 5b pop %ebx
4b5: 81 c3 00 11 00 00 add $0x1100,%ebx
4bb: e8 90 fe ff ff call 350 <__do_global_dtors_aux>
4c0: 59 pop %ecx
4c1: 5b pop %ebx
4c2: c9 leave
4c3: c3 ret

我们看到如下的代码:

.PLT项的内容分析

.PLT项的内容分析

结论:

  • PLT 实际为代码段. 不是简单的数据段. 首先其内容不会变. 且内容的确为一些JMP相关的代码。
  • PLT的第一项地址为: 0x0000030C (这个是基于VMA基地址为0的虚拟地址)
  • 上面的ebx的值实际为.got.plt段的基地址.(后面会证明)
  • 使用.got.plt表进行间接寻址时,是从第4项开始的,即前三项进行了跳过。
  • .got.plt 表项的大小为 4字节
  • addtwo 作为一个内部和外部都会调用的函数.在PLT中有自己的表项
  • 注意: 而同时也作为外部函数的addthree 并没有自己的PLT项

验证 ebx的内容是.got.plt基地址

我 们先看一下.got的段信息。

1
2
3
4
5
6
7
# 命令: objdump -j .got -h add.so
add.so: file format elf32-i386

Sections:
Idx Name Size VMA LMA File off Algn
18 .got 0000000c 000015a8 000015a8 000005a8 2**2
CONTENTS, ALLOC, LOAD, DATA

我们发现.got段的大小为12字节. 偏移地址为:0x000015a8,紧接着我们看一下.got.plt的信息。

1
2
3
4
5
6
7
8
# 命令 objdump -j .got.plt  -h add.so

add.so: file format elf32-i386

Sections:
Idx Name Size VMA LMA File off Algn
19 .got.plt 00000018 000015b4 000015b4 000005b4 2**2
CONTENTS, ALLOC, LOAD, DATA

重点: .got.plt的VMA是:0x000015b4;这个好像并不能说明什么。别急,我们看一下ebx的内容是怎么得到的,先看一下addthree中对addtwo的调用部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
0000040c <addtwo>:
40c: 55 push %ebp
40d: 89 e5 mov %esp,%ebp
40f: 83 ec 10 sub $0x10,%esp
412: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
419: 8b 45 0c mov 0xc(%ebp),%eax
41c: 8b 55 08 mov 0x8(%ebp),%edx
41f: 8d 04 02 lea (%edx,%eax,1),%eax
422: 89 45 fc mov %eax,-0x4(%ebp)
425: 8b 45 fc mov -0x4(%ebp),%eax
428: c9 leave
429: c3 ret

0000042a <addthree>:
42a: 55 push %ebp
42b: 89 e5 mov %esp,%ebp
42d: 53 push %ebx
42e: 83 ec 24 sub $0x24,%esp
;====:
431: e8 d1 ff ff ff call 407 <__i686.get_pc_thunk.bx> ;
436: 81 c3 7e 11 00 00 add $0x117e,%ebx
43c: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
443: 8b 45 0c mov 0xc(%ebp),%eax
446: 89 44 24 04 mov %eax,0x4(%esp)
44a: 8b 45 08 mov 0x8(%ebp),%eax
44d: 89 04 24 mov %eax,(%esp)
450: e8 d7 fe ff ff call 32c <addtwo@plt>
455: 03 45 10 add 0x10(%ebp),%eax
458: 89 45 f4 mov %eax,-0xc(%ebp)
45b: 8b 45 f4 mov -0xc(%ebp),%eax
45e: 83 c4 24 add $0x24,%esp
461: 5b pop %ebx
462: 5d pop %ebp
463: c3 ret

我们对计算的部分进行一下标注:

在call addtwo@plt之前.ebx的值已经计算好为: 0x15B4
  • 与预期一致,因为这个库里面的所有的代码都是需要重定位的,没有静态可以确定位置的。

再看一下动态段的重定位表:

另外一个引用PLT表的地方:EBX仍然值为0x15B4

因此我们可以确定,当所有的调用到.plt段的时候,ebx都会提前初始化为.got.plt的段基地址。而在上面的PLT的表项中的代码中,在引用EBX进行JMP的时候,还会跟上一个表内偏移。

  • 第一个PLT项是一个common的项。没有用于跳转找目标函数。
  • 从第二个PLT项开始安排为跳转项。每一个PLT项的大小为:16字节。因为每个表项之间的偏移为16字节。
  • PLT引用的.got.plt段前三个entry都没有用到. 根据书中的内容我们可知:

.got.plt中的前三项内容

重定位表

我们看一下重定位表的内容,按道理重定位表应该会对.got.got.plt的内容进行重定位,因为动态链接库的装载地址是不确定的。因此.got项和.got.plt的内容都需要在装载时重定位。

先看一下非动态段的重定位表:

没有需要重定位的的项
  • 与预期一致,因为这个库里面的所有的代码都是需要重定位的。没有静态可以确定位置的。

再看一下动态段的重定位表:

一样与预期一致

解读一下:

  • 同样没有函数: addthree 的符号的相关的重定位项. (并不代表addthree不需要重定位. 这是两个概念.)
  • 里面有三个R_386_JUMP_SLOT类型的重定位项. 三个项的地址分别是:
    • 000015c0: R_386_JUMP_SLOT
    • 000015c4: R_386_JUMP_SLOT
    • 000015c8: R_386_JUMP_SLOT

我们知道.got.plt的首地址是: 0x15B4。而第一个JUMP_SLOT的地址是:15B4+4*3 = 15C0 和前面分析的跳过了前三项got.plt项保持一致。

为什么没有addthreeplt

  • 这个问题其实非常重要。它将揭示.plt表项的本质。以及结合上面的PLT项是代码还是数据结合看就可以得出结论了。

虽然我们的动态链接库同时导出了两个函数:addtwoaddthree;但是并不是两个函数都出现在了.plt中。而且只有一个被内部自己调用的addtwo函数有一个.plt表项. 这似乎隐约在说明一个问题:

  • .plt段是一个胶水代码段,他属于调用方,不属于被调用方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

上面猜测是属于调用方,那似乎应该大概在main函数的主模块里面会有对应的```.plt```段,并且有相应的```addtwo```函数和```addthree```函数的入口,我们先来看一下:

### ```main```模块中的```got.plt```

下面是主模块中的```.plt```段,果不其然,我们在其中发现了两个```.plt```项:

* ```addtwo@plt``` : ```0x08048400```
* ```addthree@plt``` ```0x08048410```

---


* 由于在主程序模块中,不需要动态重定位,因此这些代码加载到内存虚拟空间的地址是确定的。

![](/image/post/transship/0005/8.jpg)
<center>主程序模块中的.plt段</center>

这样就可以进一步的确认:```.plt```模块是属于调用方,因为我们在调用方的代码中找到了两个动态模块中的函数的```.plt```入口,而在动态模块中并没有完整的两个入口。

* 这其实与之前理解的plt可能属于数据段,然后由于延时加载的需要在```.got```的基础上,又加了一级```.plt```的**间接跳转**。这样这个```.plt```因为作用与```.got```类似。```.got```是一个全局偏移表,那似乎可以被所有的模块共用(现在还不能确定,后面会论证)。那```.plt```也一样,就像把一级指针的```.got```变成了两级指针的```.plt```加```.got```了。这个理解似乎没有什么问题,如果```.plt```是数据段的话。只是又加了一级对```.got```的间接寻址而已。如果```.plt```是全局共享的话,那其它模块在引用它的时候。需要:
- 要么绝对寻址地从一个地方call调用跳转到相应的```.plt```的地方。由于是绝对地址,而```.plt```有加载地址是不确定的。是动态的。那只有**加载时重定位** 的方式才能实现。那这个方式就决定了只有主模块在使用加载时重定位的方式进行修改地址。但是这个只能主模块适用。如果是其它的模块。比如另外的动态模块,那动态模块就必须得修改代码段才能正确引用到```.plt```的地址,这样就无法实现PIC的代码了,**因此结论不成立**。
- 如果是使用相对寻址。那就需要相对寻址的两者之间的地址不能变化。否则就不能相对寻址了。那这样其它模块与 这个动态模块的地址之间就产生了关联。违背了**动态模块地址无关性**的PIC模式。因此**结论也不成立**。

这样:不管使用怎样的地址定位方式,其它模块都无法友好的使用当前动态模块的```.plt```入口,因此,```.plt```必不属于被调用方,它应该是调用方自己的一个附属的胶水代码。

## ```.got.plt```段是否为进程全局唯一共享

这样我们论证完```.plt```的归属后,接下来的问题就是确定```.got```段的唯一性与归属性的问题。
* 先说一个引子:
- 问:我们最初成立```.got```段的目的是什么?
- 答:把代码中引用一个不确定的地址的情况下,需要重定位修改代码部分的重定位过程导致代码段不可变不成立的:地址引用,将其从代码中剥离出来。单独放到数据段。就像代码段中对一个变量的绝对地址引用,修改为从数据段的一个地址指针的间接引用,这样才能保证代码段的内容始终保持不变。然后数据段```.GOT```的内容动态的重定位到相应的目标符号。

其实有了上面一段描述的推理后,那```.got```是否属于进程中所有共享对象统一使用。这个结论已经很明确了。那就是```.got```段是属于模块自身的一个数据段,而不是所有的模块共享,从上面的```.got```段会被一个代码段的地址相对地址引用,那就决定了```.got```必须与相对应的代码段保持稳定的相对距离,这样才能被代码用相对地址定位的方式进行访问到。而另外一方面,不同的代码段之间的相对位置又是不确定的。这就间接说明了,不同共享对象的之间的代码段的相对地址不确定,所以不同的代码段(不同的模块)之间使用的```.got```不是同一个表。虽然```.got```被称作```全局偏移表:global offset table```,但是他只属于模块本身。

### main中的```.got.plt```地址:

同样先看一下```.got```

![](/image/post/transship/0005/9.jpg)
<center>main模块中的.got段的信息</center>

我们可以看到的确在main模块中有```.got```段且其虚拟地址在这里已经确定下来:```080497c4```,这个与动态模块中的```.got```段的虚拟地址不一样,其虚拟地址为:```000015a8```,这两个的地址完全不一样。

再看一下我们的目标:```.got.plt```

![](/image/post/transship/0005/10.jpg)
<center>main模块中的.got.plt</center>

同样我们看一下main中对```.got.plt```的引用有一些不一样:

```asm
Disassembly of section .plt:

080483d0 <__gmon_start__@plt-0x10>:
80483d0: ff 35 cc 97 04 08 pushl 0x80497cc
80483d6: ff 25 d0 97 04 08 jmp *0x80497d0
80483dc: 00 00 add %al,(%eax)
...

080483e0 <__gmon_start__@plt>:
80483e0: ff 25 d4 97 04 08 jmp *0x80497d4
80483e6: 68 00 00 00 00 push $0x0
80483eb: e9 e0 ff ff ff jmp 80483d0 <_init+0x30>

我们发现:

  1. 在main中对.got.plt的引用不再使用ebx寄存器进行基址相对寻址。
  2. 使用的第一个.got.plt项是0x80497d4

而我们上面说.got.plt的基地址是:080497c8,那前三项是被跳过了的即:080497c8,080497cc,080497d0,然后从0x80497d4开始,与前面所说的跳过前三项用于加载当前模块的动态依赖相关的信息有关系。

到此。所有的疑问都很清晰了,还有一点需要我们探究一下。那就是为什么main模块中对.got.plt引用不是使用的ebx进行寻址。而add.so中却是如此,其实啊,这个与两个模块的虚拟地址的确定与否有关系。

  • 主模块的加载虚拟地址是确定的。它是加载到进程中的第一个模块。他的地址在编译链接时就已经确认。在加载到内存的时候直接按照program header的信息加载到内存即可。因此:模块代码与got相关的位置是固定的,且是确定的。那在进行寻址的时候,直接使用一个固定的地址的间接引用即可。(即上面的:jmp *0x80497d0)
  • 而动态模块在加载的时候其加载基地址是不确定的,因此需要使用ebx去动态的获取到当前代码加载的地址。这样才能够满足动态加载的需求。

最后总结一下:plt是属于调用方的一段胶水代码。有多少个模块就可能存在多少个.plt段,而这个.plt段还是单独为这一个模块服务的。比如模块a调用了模块b。那a中就会有一个单独的.plt属于模块a用来间接寻址模块b。且这个.plt段会一个配套的.got.plt段来存储最后的真正的地址。那如果还有一个模块c也调用了模块b。那c需要一个自己的``.plt.got.plt```来完成相同的工作。

由于有全局符号介入的机制的存在,即使是自己调用自己,那也需要在自己的模块里面生成一段.plt代码和相应的.got.plt入口来完成对自己的代码的“相对寻址”,虽然明明知道自己要调用的代码与自己的相对位置是多少。但是我们就是偏偏不能那样做。因为由于全局符号介入的关系,我们最终调用的符号自己,可能不真的我们自己。

下面我画了一个图来总结呈现下这个关系吧,以防止你和我一样被绕晕:

不同模块之间的.plt和.got.plt 之间的关系

结尾

最后我们还是以问题结束吧:

  • 动态库中的PLT表与主程序中的PLT表是否为同一份?
    • 答:不是同一份
  • 动态库中的GOT.PLT表与主程序中的GOT.PLT表是否为同一份?
    • 答:同样不是同一份
  • PLT表是代码还是数据?
    • 这个是代码,不是数据。虽然<自我修养>中说是一个个的项
  • 动态库内部函数之间的调用为何不直接使用相对地址调用?
    • 因为有全局符号介入的存在.导致实际调用有可能不是调用自己的函数的情况。
      上面并没有论证.got是否为同一个,看完上面的内容,或许你已经有了答案。

参考

[1] Linux动态链接为什么要用PLT和GOT表?
[2] ELF 文件 PLT 和 GOT 静态分析