静态链接的整个过程分两步,第一步是空间与地址分配,第二步是符号解析与重定位;其中第二步是链接过程的核心,特别是重定位。在重定位过程中,重定位表和符号表起着至关重要的作用,重定位表确定了需要被重定位的地址,符号表决定了被置换成什么值。
文章内容将以下面的 hello.c 程序为例展开
-
#include <stdio.h>
-
-
int foo(int x)
-
{
-
printf("%d\n", x);
-
-
return 0;
-
}
-
-
int main(void)
-
{
-
foo(5);
-
-
return 0;
-
}
执行命令gcc -c hello.c -o test.o 生成目标文件 test.o
一、可重定位表
连接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件表里面,对于每个须要重定位的代码段和数据段,都会有一个相应的重定位表,例如 .rel.text 表对应.text段。也就是说,重定位表记录了须要被重定位的地址都在相应段的哪些地方。
执行命令 readelf -a test.o 可以看到 .text 的重定位表 .rel.text
-
重定位节 '.rel.text' 位于偏移量 0x43c 含有 3 个条目:
-
Offset Info Type Sym.Value Sym. Name
-
00000010 00000501 R_386_32 00000000 .rodata
-
00000015 00000a02 R_386_PC32 00000000 printf
-
00000031 00000902 R_386_PC32 00000000 foo
<图一>
执行 objdump -dS test.o 可以得到test.o 的反汇编代码,如下
-
00000000 <foo>:
-
0: 55 push %ebp
-
1: 89 e5 mov %esp,%ebp
-
3: 83 ec 18 sub $0x18,%esp
-
6: 8b 45 08 mov 0x8(%ebp),%eax
-
9: 89 44 24 04 mov %eax,0x4(%esp)
-
d: c7 04 24 00 00 00 00 movl $0x0,(%esp)
-
14: e8 fc ff ff ff call 15 <foo+0x15>
-
19: b8 00 00 00 00 mov $0x0,%eax
-
1e: c9 leave
-
1f: c3 ret
-
-
00000020 <main>:
-
20: 55 push %ebp
-
21: 89 e5 mov %esp,%ebp
-
23: 83 e4 f0 and $0xfffffff0,%esp
-
26: 83 ec 10 sub $0x10,%esp
-
29: c7 04 24 05 00 00 00 movl $0x5,(%esp)
-
30: e8 fc ff ff ff call 31 <main+0x11>
-
35: b8 00 00 00 00 mov $0x0,%eax
-
3a: c9 leave
-
3b: c3 ret
<图二>
1> offset 字段解释
offset 是重定位入口的偏移。例如,在图一中,符号printf的offset值为015,在<图二>,.text便宜地址0x15存储的内容为0xfc ff ff ff 。
2> info 字段解释
该字段表示重定位入口的类型和符号。该值的低8位表示重定位入口的类型, 高24位表示重定位入口的符号在符号表重的下标。
2.1 重定位入口的类型
宏定义
|
值
|
重定位修正方法
|
R_386_32
|
1
|
绝对地址修正 S+A
|
R_386_PC32
|
2
|
相对地址修正 S+A-P
|
<表一>
A = 保存在被修正位置的值(例如本例中printf重定位处的值是0xfc ff ff ff)
P = 被修正的位置(相对于段开始的偏移量或者虚拟地址)
S = 符号的实际地址, 即由info的高24位指定的符号的实际地址
2.2 高24位表示在符号表中的下标
-
Symbol table '.symtab' contains 12 entries:
-
Num: Value Size Type Bind Vis Ndx Name
-
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
-
1: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
-
2: 00000000 0 SECTION LOCAL DEFAULT 1
-
3: 00000000 0 SECTION LOCAL DEFAULT 3
-
4: 00000000 0 SECTION LOCAL DEFAULT 4
-
5: 00000000 0 SECTION LOCAL DEFAULT 5
-
6: 00000000 0 SECTION LOCAL DEFAULT 7
-
7: 00000000 0 SECTION LOCAL DEFAULT 8
-
8: 00000000 0 SECTION LOCAL DEFAULT 6
-
9: 00000000 32 FUNC GLOBAL DEFAULT 1 foo
-
10: 0000000 0 NOTYPE GLOBAL DEFAULT UND printf
-
11: 00000020 28 FUNC GLOBAL DEFAULT 1 main
<图三>
在本例中,符号printf的info值的高24位是0x00000a,对应的十进制是10;由<图三>可知,下标是10的表项对应的符号是printf,由此可验证info的高24位是符号在符号表中的下标。(重定位表中为什么要和符号表相关连呢? 因为重定位表只是能指定什么地方需要被重定位,但需要被置换成什么数值,这个是由符号表决定的)。
阅读(8464) | 评论(0) | 转发(0) |