把系统库和自己编写的代码分割成两个独立的模块,等程序真正运行时再把两个模块进行链接,这种在运行或加载时,在内存中完成链接的过程叫动态链接。节省了内存空间,并且内存中一个系统库可以被多个程序共同使用,也节省了内存空间。
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
1078
:
48
8d
3d
ca
00
00
00
lea rdi,[rip
+
0xca
]
107f
: ff
15
53
2f
00
00
call QWORD PTR [rip
+
0x2f53
]
1085
: f4 hlt
1086
:
66
2e
0f
1f
84
00
00
cs nop WORD PTR [rax
+
rax
*
1
+
0x0
]
108d
:
00
00
00
0000000000001090
<deregister_tm_clones>:
1090
:
48
8d
3d
79
2f
00
00
lea rdi,[rip
+
0x2f79
]
1097
:
48
8d
05
72
2f
00
00
lea rax,[rip
+
0x2f72
]
109e
:
48
39
f8
cmp
rax,rdi
10a1
:
74
15
je
10b8
<deregister_tm_clones
+
0x28
>
10a3
:
48
8b
05
36
2f
00
00
mov rax,QWORD PTR [rip
+
0x2f36
]
10aa
:
48
85
c0 test rax,rax
10ad
:
74
09
je
10b8
<deregister_tm_clones
+
0x28
>
10af
: ff e0 jmp rax
10b1
:
0f
1f
80
00
00
00
00
nop DWORD PTR [rax
+
0x0
]
-
-
0000000000001149
<main>:
1149
: f3
0f
1e
fa endbr64
114d
:
55
push rbp
114e
:
48
89
e5 mov rbp,rsp
1151
:
48
83
ec
10
sub rsp,
0x10
1155
: c7
45
fc
64
00
00
00
mov DWORD PTR [rbp
-
0x4
],
0x64
115c
:
48
8d
45
fc lea rax,[rbp
-
0x4
]
1160
:
48
8d
15
a9
2e
00
00
lea rdx,[rip
+
0x2ea9
]
1167
:
48
89
d6 mov rsi,rdx
116a
:
48
89
c7 mov rdi,rax
1172
: b8
00
00
00
00
mov eax,
0x0
1177
: c9 leave
1178
: c3 ret
可以加载而且无需重定位的代码被称为位置无关代码(PIC),它是共享库必有属性。gcc的-fpic参数可生成PIC。通过pic共享库代码可以被无限多个进程共享,从而节约内从资源。
一个程序的数据段和代码段的相对距离总是保持不变的,因此,指令和变量之间的距离是一个运行时常量,与绝对内存地址无关,因此便有了全局偏移量表。它位于数据段的开头,用于保存全局变量和库函数的引用,每个条目占8个字节,在加载时会进行重定位并填入符号的绝对地址。
为了引入RELRO保护机制,GOT被拆分为.got节和.got.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
func.so: 文件格式 elf64
-
x86
-
64
节:
Idx Name Size VMA LMA
File
off Algn
0
.note.gnu.
property
00000020
00000000000002a8
00000000000002a8
000002a8
2
*
*
3
CONTENTS, ALLOC, LOAD, READONLY, DATA
1
.note.gnu.build
-
id
00000024
00000000000002c8
00000000000002c8
000002c8
2
*
*
2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2
.gnu.
hash
00000030
00000000000002f0
00000000000002f0
000002f0
2
*
*
3
CONTENTS, ALLOC, LOAD, READONLY, DATA
3
.dynsym
000000c0
0000000000000320
0000000000000320
00000320
2
*
*
3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4
.dynstr
00000065
00000000000003e0
00000000000003e0
000003e0
2
*
*
0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5
.rela.dyn
000000c0
0000000000000448
0000000000000448
00000448
2
*
*
3
CONTENTS, ALLOC, LOAD, READONLY, DATA
6
.init
0000001b
0000000000001000
0000000000001000
00001000
2
*
*
2
CONTENTS, ALLOC, LOAD, READONLY, CODE
7
.plt
00000010
0000000000001020
0000000000001020
00001020
2
*
*
4
CONTENTS, ALLOC, LOAD, READONLY, CODE
8
.plt.got
00000010
0000000000001030
0000000000001030
00001030
2
*
*
4
CONTENTS, ALLOC, LOAD, READONLY, CODE
9
.text
000000f6
0000000000001040
0000000000001040
00001040
2
*
*
4
CONTENTS, ALLOC, LOAD, READONLY, CODE
10
.fini
0000000d
0000000000001138
0000000000001138
00001138
2
*
*
2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11
.eh_frame_hdr
00000024
0000000000002000
0000000000002000
00002000
2
*
*
2
CONTENTS, ALLOC, LOAD, READONLY, DATA
12
.eh_frame
0000007c
0000000000002028
0000000000002028
00002028
2
*
*
3
CONTENTS, ALLOC, LOAD, READONLY, DATA
13
.init_array
00000008
0000000000003e78
0000000000003e78
00002e78
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
14
.fini_array
00000008
0000000000003e80
0000000000003e80
00002e80
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
15
.dynamic
00000150
0000000000003e88
0000000000003e88
00002e88
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
16
.got
00000028
0000000000003fd8
0000000000003fd8
00002fd8
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
17
.got.plt
00000018
0000000000004000
0000000000004000
00003000
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
18
.data
0000000c
0000000000004018
0000000000004018
00003018
2
*
*
3
CONTENTS, ALLOC, LOAD, DATA
19
.bss
0000000c
0000000000004024
0000000000004024
00003024
2
*
*
2
ALLOC
20
.comment
0000002b
0000000000000000
0000000000000000
00003024
2
*
*
0
CONTENTS, READONLY
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
00000000000010f9
<func>:
10f9
: f3
0f
1e
fa endbr64
10fd
:
55
push rbp
10fe
:
48
89
e5 mov rbp,rsp
1101
:
48
89
7d
f8 mov QWORD PTR [rbp
-
0x8
],rdi
1105
:
48
89
75
f0 mov QWORD PTR [rbp
-
0x10
],rsi
1109
:
48
8b
45
f8 mov rax,QWORD PTR [rbp
-
0x8
]
110d
:
8b
10
mov edx,DWORD PTR [rax]
110f
:
48
8b
05
c2
2e
00
00
mov rax,QWORD PTR [rip
+
0x2ec2
]
1116
:
89
10
mov DWORD PTR [rax],edx
1118
:
48
8b
45
f0 mov rax,QWORD PTR [rbp
-
0x10
]
111c
:
8b
10
mov edx,DWORD PTR [rax]
111e
:
48
8b
45
f8 mov rax,QWORD PTR [rbp
-
0x8
]
1122
:
89
10
mov DWORD PTR [rax],edx
1124
:
48
8b
05
ad
2e
00
00
mov rax,QWORD PTR [rip
+
0x2ead
]
112b
:
8b
10
mov edx,DWORD PTR [rax]
112d
:
48
8b
45
f0 mov rax,QWORD PTR [rbp
-
0x10
]
1131
:
89
10
mov DWORD PTR [rax],edx
1133
:
90
nop
1134
:
5d
pop rbp
1135
: c3 ret
可以看到全局变量tmp位于GOT上,R_X86_64_GLOB_DAT表示需要动态链接器找到tmp值并填充到0x3fd8。在func()函数取出tmp时计算符号相对PC的偏移rip+0x2ec2,也就是0x1116+0x2ec2=0x3fd8,同理,0x112b+0x2ead=0x3fd8.
延迟绑定的基本思想是当函数第一次被调用时,动态链接器才进行符号查找,重定位等操作,如果未被调用则不进行绑定。它是为了解决需重定位符号过多影响性能的问题。
ELF文件通过过程链接表(PLT)和GOT配合来实现延迟绑定,每个被调用的库函数都有一组对应的PLT和GOT。位于代码段.plt节的PLT是一个数组,为个条目占16个字节。PLT[0]用于跳转到动态链接器,PLT[1]用于调用系统启动函数_libc_start_main(),我们熟悉的main()函数就是在这里面调用的,从PLT[2]开始就是被调用的各个函数条目。位于数据段的.got.plt节的GOT也是一个数组,每个条目占8字节。其中GOT[0]和GOT[1]包含动态链接器在解析函数地址时所需要的两个地址(.dynamic和relor条目)。GOT[2]是动态链接器(ld-linux.so)的入口点,从GOT[3]开始就是被调用的各个函数条目,这些条目默认指向PLT条目的第二条指令,完成绑定后才会被修改为函数的实际地址。