为什么具有对_GLOBAL_OFFSET_TABLE_的非限定引用的nasm程序集显然可以组装并链接为PIC?

问题描述 投票:2回答:1

为什么我可以组合和链接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());
}

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

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生成链接器会阻塞的重定位。

assembly x86 nasm elf position-independent-code
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动态链接可以在映射共享库之后应用fixup。但显然它可以。 (最好不要这样做,因为它会弄脏内存页面,因此它不再仅仅受磁盘上文件的支持。)

但我检查了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也将64位绝对重定位链接到PIE可执行文件中。 (PIE可执行文件是ELF共享对象)。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.