2

为什么get_got.asm当它包含对其 GOT 的绝对地址的引用时,我可以将其作为与位置无关的代码进行汇编和链接?

get_got.asm

extern _GLOBAL_OFFSET_TABLE_

section .text
global get_got
get_got:
        mov     rax, _GLOBAL_OFFSET_TABLE_
        ret

主程序

#include <stdio.h>

void* get_got(void);

int main(int argc, char* argv[]) {
    printf("%p\n", get_got());
}

组装、编译、链接、运行:

nasm -felf64 -o get_got.o get_got.asm
gcc -fPIC -shared -o get_got.so get_got.o
gcc -Wl,-rpath=\$ORIGIN -o main main.c get_got.so
./main
0x148ba1cba000

这里发生了什么?在我看来, get_got.so 不知何故有一个 GOT 的绝对地址,直到运行时才知道地址。反汇编 get_got.so 表明 mov 实际上包含一个立即数 (0x201000)。显然我对某事有一个重大的误解。我预计这会导致 nasm 生成链接器会阻塞的重定位。

4

1 回答 1

3

我使用lea rax, [rel _GLOBAL_OFFSET_TABLE_].

我区分了readelf -a输出。但是,来自不同地址的噪音很多。
readelf -a get_got.so | diff -u - <(readelf -a get_got_rel.so) | less

最有趣的区别是:

--- readelf -a get_got.so
+++ readelf -a get_got_rel.so
....

-Dynamic section at offset 0xe40 contains 22 entries:
+Dynamic section at offset 0xe50 contains 21 entries:
...

- 0x0000000000000016 (TEXTREL)            0x0
  0x000000006ffffffe (VERNEED)            0x3b0
  0x000000006fffffff (VERNEEDNUM)         1
  0x000000006ffffff0 (VERSYM)             0x398
- 0x000000006ffffff9 (RELACOUNT)          4
+ 0x000000006ffffff9 (RELACOUNT)          3

所以绝对版本有一个文本重定位。我不知道 Linux / ELF 动态链接可以在映射共享库后应用修复。但显然可以。(最好不要这样做,因为它弄脏了内存页面,所以它不再只是由磁盘上的文件支持。)

但我检查了 GDB,这就是发生的事情:在其中设置一个断点get_got并运行它:

(gdb) disas
Dump of assembler code for function get_got:
=> 0x00007f9e77b235b0 <+0>:     movabs rax,0x7f9e77d24000
   0x00007f9e77b235ba <+10>:    ret    

objdump -dRC -Mintel get_got.so: (注意没有换行-w):

00000000000005b0 <get_got>:
 5b0:   48 b8 00 10 20 00 00    movabs rax,0x201000
 5b7:   00 00 00 
                        5b2: R_X86_64_RELATIVE  *ABS*+0x201000
 5ba:   c3                      ret    

感谢@Jester 的-R提示;我通常使用objdump -dr ..., not -R,并且小写的 r 不会为.so.
get_got.o-r显示movabs rax,0x0 2: R_X86_64_64 _GLOBAL_OFFSET_TABLE_


gcc -nostdlib -pie也将 64 位绝对重定位链接到 PIE 可执行文件。(PIE 可执行文件ELF 共享对象)。

PIC / PIE 中不允许的是 32 位绝对重定位:x86-64 Linux 中不再允许使用 32 位绝对地址?. 您收到链接器错误。像这样的寻址模式array[rcx*4]在 PIC/PIE 代码中不可用,您需要单独的指令将地址放入寄存器。

lea rdi, [rel array]64 位立即绝对值更好的选择,因为它更小,对 uop 缓存更友好,并且在加载时不需要修复。

于 2017-12-18T17:34:52.597 回答