我写了简单的共享库:
extern void some_func(void);
void
function(void)
{
some_func();
}
编译/内置:
gcc -fPIC -mcmodel=large -c test.c -o test.o
gcc -fPIC -shared test.o -o libtest.so
反汇编,看看some_func是如何被引用的:
$ objdump -d libtest.so
00000000000006a0 <function>:
6a0: 55 push %rbp
6a1: 48 89 e5 mov %rsp,%rbp
6a4: 41 57 push %r15
6a6: 48 83 ec 08 sub $0x8,%rsp
6aa: 48 8d 05 f9 ff ff ff lea -0x7(%rip),%rax # 6aa <function+0xa>
6b1: 49 bb 56 09 20 00 00 movabs $0x200956,%r11
6b8: 00 00 00
6bb: 4c 01 d8 add %r11,%rax
6be: 49 89 c7 mov %rax,%r15
6c1: 48 ba 80 f5 df ff ff movabs $0xffffffffffdff580,%rdx
6c8: ff ff ff
6cb: 48 01 c2 add %rax,%rdx
6ce: ff d2 callq *%rdx
6d0: 90 nop
6d1: 48 83 c4 08 add $0x8,%rsp
6d5: 41 5f pop %r15
6d7: 5d pop %rbp
6d8: c3 retq
查看.got.plt
所在的位置:
$ readelf -S libtest.so
...
[21] .got.plt PROGBITS 0000000000201000 00001000
0000000000000020 0000000000000008 WA 0 0 8
...
什么是搬迁:
$ readelf -r libtest.so
Relocation section '.rela.plt' at offset 0x538 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000201018 000400000007 R_X86_64_JUMP_SLO 0000000000000000 some_func + 0
在6aa
- 6bb
中,我们得到GOT的绝对位置:6aa + 0x200956 = 0x201000
这与readelf -S libtest.so
的输出一致。
我们在GOT(与函数相关)中跳过3个保留字节,并确定some_func的绝对地址应该在运行时在+ 0x18(来自GOT的第四个字节)处找到。
同意readelf -r libtest.so
。
但objdump反汇编中的6c1指令显示:
movabs $0xfff...dff580, %rdx
我希望源操作数将保持+0x18
(从GOT偏移,其地址位于rax
),但它有一些大的负数。
你能解释一下它显示的数字,但不是0x18
吗?
答案 0 :(得分:2)
有两种重定位:静态和动态(1);一个用于静态链接器ld
,另一个用于加载器(动态链接器,rtld) - ld-linux.so.2
用于linux的glibc 2. *(选中Dynamic Linking and Loading, 1999或Static Linkers and Dyanmic Link Loaders)。
使用objdump
转储重定位时,静态重定位有-r
选项,动态重定位有-R
。
您的案例不仅仅是GOT,而是GOT.PLT - 用于程序链接的GOT。这种访问使用动态重定位。因此,您应该检查objdump -dR libtest.so
的输出,它将显示其中的反汇编和动态重定位。
readelf -r libtest.so
的引用行仅适用于PLT表,而不适用于代码。
http://www.airs.com/blog/archives/41
或函数调用,程序链接器将设置一个PLT条目,如下所示:
jmp *offset(%ebx) pushl #index jmp first_plt_entry
程序链接器将在GOT中为每个条目分配一个条目 PLT。它将为类型的GOT条目创建动态重定位 JMP_SLOT。它会将GOT条目初始化为的基址 共享库加上代码中第二条指令的地址 上面的顺序。当动态链接器执行初始延迟绑定时 在JMP_SLOT reloc上,它只会添加之间的区别 共享库加载地址和共享库基地址 GOT条目。结果是第一条jmp指令将跳转到 第二条指令,它将推送索引条目和分支 第一个PLT条目。第一个PLT条目是特殊的,看起来像这样:
pushl 4(%ebx) jmp *8(%ebx)
这引用了GOT中的第二个和第三个条目。动态 链接器将初始化它们以具有适当的回调值 进入动态链接器本身。动态链接器将使用索引 由第一个代码序列推送以查找JMP_SLOT重定位。 当动态链接器确定要调用的函数时,它将会 将函数的地址存储到GOT条目引用中 第一个代码序列。因此,下次调用函数时, jmp指令将直接分支到正确的代码。