2017-04-12 65 views
4

64位Linux默认使用小内存模式,它将所有代码和静态数据放在2GB地址限制以下。这确保您可以使用32位绝对地址。老版本的gcc为静态数组使用32位绝对地址,以便为相对地址计算节省额外的指令。但是,这不再有效。如果我尝试在程序集中创建一个32位绝对地址,则会出现链接器错误: “在创建共享对象时无法使用针对`.data'的重定位R_X86_64_32S;使用-fPIC重新编译”。 这个错误信息当然是误导性的,因为我没有制作共享对象,-fPIC也没有帮助。 目前为止我发现的是:gcc版本4.8.5使用静态数组的32位绝对地址,gcc版本6.3.0不使用。版本5可能不会。 binutils 2.24中的链接器允许使用32位绝对地址,而2.28版本则不允许。x86-64 Linux中不再允许32位绝对地址?

这种变化的结果是,老图书馆已经重新编译和传统的汇编代码被打破。

现在我要问:如果是这种变化做?它是否记录在某处?有没有一个链接器选项,使其接受32位绝对地址?

回答

7

你的发行版配置的gcc与--enable-default-pie,所以它在默认情况下做出与位置无关的可执行文件,(允许可执行的ASLR以及图书馆)。 PIC/PIE不允许搬迁。

使用gcc -fno-pie -no-pie覆盖此回旧的行为。-no-pie是链接器选项-fno-pie is the code-gen option。仅使用-fno-pie时,gcc将使编码类似mov eax, offset .LC0,但不与仍然启用的-pie链接。

可以在默认情况下启用了PIE,太:clang -fno-pie -nopie一个July 2017 patch做出-no-pie一个别名-nopie,为COMPAT用gcc,但clang4.0.1没有它。)


由于只有-no-pie,(但仍然-fpie)编译器生成的代码会比必要慢,但是仍然会被链接到不从ASLR受益位置相关的可执行文件。惩罚主要是在访问全局变量时(而不是使用静态存储类的static文件作用域变量)。请注意,对于64位代码,-fPIE不如-fPIC差,但对于32位仍然不好。见some examples on the Godbolt compiler explorer,并参见The sorry state of dynamic libraries on Linux

如果你的GCC是这样配置的,gcc -v |& grep -o -e '[^ ]*pie'打印--enable-default-pie。在early 2015的gcc中添加了对此配置选项的支持。 Ubuntu在16.10启用它,并且在gcc 6.2.0-7的同一时间Debian(导致内核构建错误:https://lkml.org/lkml/2016/10/21/904)。相关:Build compressed x86 kernels as PIE


请注意,ld本身并没有改变它的默认值。它仍然正常工作(至少在Arch Linux上使用binutils 2.28)。这种变化是gcc默认为传递-pie作为一个连接器选项,除非你明确地使用-static-no-pie

在NASM源文件中,我使用了a32 mov eax, [abs buf]来获取绝对地址。 (我测试,如果6个字节的方式来编码小的绝对地址(地址大小+ MOV EAX,总督们:67 a1 40 f1 60 00)已对英特尔CPU的LCP失速It does。)

nasm -felf64 -Worphan-labels -g -Fdwarf testloop.asm && 
ld -o testloop testloop.o    # works 

gcc -v -nostdlib testloop.o   # doesn't work 
... 
..../collect2 ... -pie ... 
/usr/bin/ld: testloop.o: relocation R_X86_64_32 against `.bss' can not be used when making a shared object; recompile with -fPIC 
/usr/bin/ld: final link failed: Nonrepresentable section on output 
collect2: error: ld returned 1 exit status 

gcc -v -no-pie -nostdlib testloop.o # works 
gcc -v -static -nostdlib testloop.o # also works: -static implies -no-pie 

相关:building static/dynamic executables with/without libc, defining _start or main


filereadelf说馅饼 “共享对象”,而不是可执行文件。

$ gcc -fno-pie -no-pie -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB executable, ... 

$ gcc -O3 hello.c 
$ file a.out 
a.out: ELF 64-bit LSB shared object, ... 

半相关:另一个最近的gcc的特点是gcc -fno-plt。最后,调用共享库可能只是call [rip + [email protected]](AT & T call *[email protected](%rip)),没有PLT蹦床。

Distros希望尽快启用它,因为它也避免了需要可写+可执行的内存页面。对于进行大量共享库调用的程序来说,这是一个显着的提速。 x86-64 clang -O2 -g在任何硬件the patch author tested on上编译tramp3d从41.6s变为36.8s。 (铿锵也许是共享库调用的最坏情况)。

它确实需要早期绑定而不是懒惰的动态链接,所以对于立即退出的大型程序来说它会更慢。 (例如clang --version或编译hello.c)。显然,这种放缓可能会因为预先链接而减少。

虽然这并不能消除PIC代码中外部变量的GOT开销。 (请参阅上面的godbolt链接)。

+1

对于'-m32'来说这真的很糟糕,因为它浪费了一个宝贵的寄存器来存放PC。我的64位发行版也启用了标志,所以'-m32'也使用它。 –

相关问题