为什么GOT输入偏移出现错误?

时间:2016-10-02 13:13:33

标签: linux relocation plt

我写了简单的共享库:

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吗?

1 个答案:

答案 0 :(得分:2)

有两种重定位:静态和动态(1);一个用于静态链接器ld,另一个用于加载器(动态链接器,rtld) - ld-linux.so.2用于linux的glibc 2. *(选中Dynamic Linking and Loading, 1999Static 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指令将直接分支到正确的代码。