为什么 nasm 带有对 _GLOBAL_OFFSET_TABLE_ 的非限定引用的程序集显然可以作为 PIC 进行组装和链接?

Why can nasm assembly with unqualified references to _GLOBAL_OFFSET_TABLE_ apparently be assembled and linked as PIC?

为什么我可以 assemble 和 link get_got.asm 作为位置无关的代码,当它包含对其 GOT 的绝对地址的引用时?

get_got.asm

extern _GLOBAL_OFFSET_TABLE_

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

main.c

#include <stdio.h>

void* get_got(void);

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

组装、编译、linking、运行:

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 生成 link 人会窒息的重定位。

我使用 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 动态 linking 可以在映射共享库后应用修正。但显然可以。 (最好不要,因为它会弄脏内存页,因此它不再仅由磁盘上的文件支持。)

但我检查了 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 ...,而不是 -R,小写的 r 不会为 .so.
打印任何重定位 在 get_got.o 上,-r 显示 movabs rax,0x0 2: R_X86_64_64 _GLOBAL_OFFSET_TABLE_


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

PIC / PIE 中不允许的是 32 位绝对重定位:32-bit absolute addresses no longer allowed in x86-64 Linux?。您收到 linker 错误。 array[rcx*4] 等寻址模式在 PIC/PIE 代码中不可用,您需要单独的指令将地址放入寄存器。

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