本文分析剩下的四种类型R_386_COPY,R_386_JMP_SLOT,R_386_RELATIVE,R_386_GOTOFF
R_386_RELATIVE 8 word32 B + A
R_386_JMP_SLOT 7 word32 S
R_386_GOTOFF 9 word32 S + A - GOT
R_386_COPY 5 none none
>>>>>>>>>>>>R_386_RELATIVE 用于局部变量,执行时重定位
查看1.c.txt中1.so的readelf
000013c8 00008 R_386_RELATIVE
000013cc 00008 R_386_RELATIVE
offset 00013c8 是地址,相对于1.so加载地址,显然是在.data节中,没有符号名,那他们是什么呢?
[ 9] .data PROGBITS 000013c8 0003c8 000008 00 WA 0 0 4
[root@proxy ~/3]# objdump -sj .data 1.so
1.so: file format elf32-i386
Contents of section .data:
13c8 b4030000 c2030000 ........
值分别是03b4和03c2,应该是位于.rodata中
[root@proxy ~/3]# objdump -sj .rodata 1.so
1.so: file format elf32-i386
Contents of section .rodata:
03b4 68656c6c 6f20576f 726c6421 0a006162 hello World!..ab
03c4 6300 c.
果然是的.
R_386_RELATIVE 8 word32 B + A,使用加载地址+Offset处值来重定位
验证:由于是执行时连接,需要启用gdb来调试看看
[root@proxy ~/3]# ldd 4
1.so => /usr/lib/1.so (0x4002a000)
libc.so.6 => /lib/i686/libc.so.6 (0x4002c000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
因此
000013c8 00008 R_386_RELATIVE
000013cc 00008 R_386_RELATIVE
加载后的地址为0x4002b3c8,0x4002b3cc
[root@proxy ~/3]# cp 1.so /usr/lib
cp: overwrite `/usr/lib/1.so'? y
[root@proxy ~/3]# gdb -q ./4
(gdb) b main
Breakpoint 1 at 0x8048596
(gdb) r
Starting program: /root/3/./4
Breakpoint 1, 0x08048596 in main ()
(gdb) x/w 0x4002b3c8
0x4002b3c8 <s>: 0x4002a3b4
(gdb) x/w 0x4002b3cc
0x4002b3cc <t>: 0x4002a3c2
(gdb)
Contents of section .data:
13c8 b4030000 c2030000 ........
分别加上0x4002a000就是上面的结果
>>>>>>>>>>>R_386_JMP_SLOT 用于函数,执行时重定位
这个简单,应该修正为符号的实际值S(例如函数f的地址)
>>>>>>>>>>>R_386_GOTOFF 用于静态变量,链接是重定位
1.o中 .rel.text节中的该类型条目有:
00000018 00309 R_386_GOTOFF 00000000 .data
该条目的意思就是GOT生成后,并在GOT表中为该静态变量分配条目后,将该条目在GOT表中的index写入到.text节中偏移地址为0000 0018 的地方。
1.o中.text节中的代码
16: ff b3 00 00 00 00 pushl 0x0(%ebx)
相应的汇编代码:
pushl s@GOTOFF(%ebx )
通过链接成.so库以后生成的结果:
生成1.so中
372: ff b3 f8 ff ff ff pushl 0xfffffff8(%ebx)
修正值为0xfffffff8,即-8,即got-8处.
[root@proxy ~/3]# objdump -sj .data 1.so
1.so: file format elf32-i386
Contents of section .data:
13c8 b4030000 c2030000 ........
即s的值为03b4,指向.rodata
[root@proxy ~/3]# objdump -sj .rodata 1.so
1.so: file format elf32-i386
Contents of section .rodata:
03b4 68656c6c 6f20576f 726c6421 0a006162 hello World!..ab
03c4 6300 c.
所以R_386_GOTOFF的修正方式是:将符号地址和GOT地址差值加上Offset处值存入Offset处.//S + A - GOT
>>>>>R_386_COPY,用于全局变量,执行时重定位
4中.rel.dyn节中有如下条目:
08049748 00105 R_386_COPY 08049748 t
该条目的意思是将.text节中08049748地方的内容修改为动态节符号节.dynsym中相应符号地址中内容。
[22] .bss NOBITS 08049748 000748 00001c 00 WA 0 0 4 //在.bss中1.so中
在.dynsym节中有如下条目:
16: 000013cc 4 OBJECT GLOBAL DEFAULT 9 t
加载地址0x4002a00,所以t的值是0x4002b3cc
abcabc[root@proxy ~/3]# gdb -q ./4
(gdb) b main
Breakpoint 1 at 0x8048596
(gdb) r
Starting program: /root/3/./4
Breakpoint 1, 0x08048596 in main ()
(gdb) x /w 0x08049748 //查4中t的值
0x8049748 <t>: 0x4002a3c2
(gdb) x /w 0x4002b3cc //查1.so中t的值
0x4002b3cc <t>: 0x4002a3c2
(gdb)
两个值相等
所以R_386_COPY的修正方式是:将解析到的符号地址处的值(大小由size决定)复制到Offset处。
这实现了每个可执行文件都有独立的全局变量,而不互相干扰。
可以发现重定位目标文件有.symtab表,这个表是必须的,虽然可以用strip去掉,但是最终连接时会出错
而动态链接库文件和可执行文件有.dynsym和.symtab,.dynsym是必须的,而.symtab不是,可以执行strip命令去掉
附件1
1.c
[root@proxy ~/3]# cat 1.c
#include <stdio.h>
static char *s="hello World!/n";
char *t="abc";
void f()
{
printf(s);
}
void g()
{
printf(t);
}
[root@proxy ~/3]# gcc -fPIC -S 1.c
[root@proxy ~/3]# cat 1.s
.file "1.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "hello World!/n"
.data
.align 4
.type s,@object
.size s,4
s:
.long .LC0
.globl t
.section .rodata
.LC1:
.string "abc"
.data
.align 4
.type t,@object
.size t,4
t:
.long .LC1
.text
.align 4
.globl f
.type f,@function
f:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $4, %esp
call .L3
.L3:
popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+[.-.L3], %ebx
subl $12, %esp
pushl s@GOTOFF(%ebx )
call printf@PLT
addl $16, %esp
movl -4(%ebp), %ebx
leave
ret
.Lfe1:
.size f,.Lfe1-f
.align 4
.globl g
.type g,@function
g:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $4, %esp
call .L5
.L5:
popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+[.-.L5], %ebx
subl $12, %esp
movl t@GOT(%ebx ), %eax
pushl (%eax)
call printf@PLT
addl $16, %esp
movl -4(%ebp), %ebx
leave
ret
.Lfe2:
.size g,.Lfe2-g
.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)"
[root@proxy ~/3]# gcc -fPIC -c 1.c -o 1.o
[root@proxy ~/3]# ld -shared 1.o -o 1.so
[root@proxy ~/3]# objdump -dj .text 1.o
1.o: file format elf32-i386
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 53 push %ebx
4: 83 ec 04 sub $0x4,%esp
7: e8 00 00 00 00 call c <f+0xc>
c: 5b pop %ebx
d: 81 c3 03 00 00 00 add $0x3,%ebx
13: 83 ec 0c sub $0xc,%esp
16: ff b3 00 00 00 00 pushl 0x0(%ebx)
1c: e8 fc ff ff ff call 1d <f+0x1d>
21: 83 c4 10 add $0x10,%esp
24: 8b 5d fc mov 0xfffffffc(%ebp),%ebx
27: c9 leave
28: c3 ret
29: 8d 76 00 lea 0x0(%esi),%esi
0000002c <g>:
2c: 55 push %ebp
2d: 89 e5 mov %esp,%ebp
2f: 53 push %ebx
30: 83 ec 04 sub $0x4,%esp
33: e8 00 00 00 00 call 38 <g+0xc>
38: 5b pop %ebx
39: 81 c3 03 00 00 00 add $0x3,%ebx
3f: 83 ec 0c sub $0xc,%esp
42: 8b 83 00 00 00 00 mov 0x0(%ebx),%eax
48: ff 30 pushl (%eax)
4a: e8 fc ff ff ff call 4b <g+0x1f>
4f: 83 c4 10 add $0x10,%esp
52: 8b 5d fc mov 0xfffffffc(%ebp),%ebx
55: c9 leave
56: c3 ret
57: 90 nop
[root@proxy ~/3]# readelf -a 1.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 328 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)