为什么GOT entry offset 出现错误?

Why GOT entry offset appears wrong?

我写了简单的共享库:

extern void some_func(void);

void
function(void)
{
        some_func();
}

Compiled/built:

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    [=12=]x8,%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 [=12=]x200956,%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 [=12=]xffffffffffdff580,%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    [=12=]x8,%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 [=15=]xfff...dff580, %rdx  

我预计源操作数将保持 +0x18(与 GOT 的偏移量,其地址位于 rax),但它有一些大的负数。

你能解释一下它显示的是什么数字而不是 0x18 吗?

有两种重定位:静态和动态 (1); one for static linker ld and other for loader (dynamic linker, rtld) - ld-linux.so.2 for linux's glibc 2.* (check Dynamic Linking and Loading, 1999 or Static Linkers and Dyanmic Link Loaders)。

当您使用 objdump 转储重定位时,它有 -r 静态重定位选项和 -R 动态重定位选项。

您的案例不仅仅是 GOT,它是 GOT.PLT - GOT 用于程序链接。这种访问使用动态重定位。因此,您应该检查 objdump -dR libtest.so 的输出,它会向您显示其中的反汇编和动态重定位。

来自 readelf -r libtest.so 的引用行仅适用于 PLT table,不适用于代码。

http://www.airs.com/blog/archives/41

or function calls, the program linker will set up a PLT entry to look like this:

jmp *offset(%ebx)
pushl #index
jmp first_plt_entry

The program linker will allocate an entry in the GOT for each entry in the PLT. It will create a dynamic relocation for the GOT entry of type JMP_SLOT. It will initialize the GOT entry to the base address of the shared library plus the address of the second instruction in the code sequence above. When the dynamic linker does the initial lazy binding on a JMP_SLOT reloc, it will simply add the difference between the shared library load address and the shared library base address to the GOT entry. The effect is that the first jmp instruction will jump to the second instruction, which will push the index entry and branch to the first PLT entry. The first PLT entry is special, and looks like this:

pushl 4(%ebx)
jmp *8(%ebx)

This references the second and third entries in the GOT. The dynamic linker will initialize them to have appropriate values for a callback into the dynamic linker itself. The dynamic linker will use the index pushed by the first code sequence to find the JMP_SLOT relocation. When the dynamic linker determines the function to be called, it will store the address of the function into the GOT entry references by the first code sequence. Thus, the next time the function is called, the jmp instruction will branch directly to the right code.