linux内核全局变量重定位,x86-64 Linux中不再允许32位绝对地址?

您的发行版使用–enable-default-pie配置gcc,因此它默认情况下使位置无关的可执行文件(允许可执行文件的ASLR以及库).如今,大多数发行版都在这样做.

你实际上是在创建一个共享对象:PIE可执行文件是一种使用具有入口点的共享对象的hack.动态链接器已经支持此功能,ASLR非常适合安全性,因此这是为可执行文件实现ASLR的最简单方法.

ELF共享对象中不允许32位绝对重定位;这将阻止它们被加载到低2GiB之外(对于符号扩展的32位地址).允许使用64位绝对地址,但通常只需要跳转表或其他静态数据,而不是指令的一部分

使用-fPIC重新编译错误消息的部分是手写asm的伪造;它是为人们使用gcc -c进行编译,然后尝试使用gcc链接gcc -shared -o foo.so * .o而使用gcc编写的,其中-fPIE不是默认值.错误消息可能应该更改,因为许多人在链接手写asm时遇到此错误.

如何使用RIP相对寻址:基础知识

对于没有缺点的简单情况,始终使用RIP相对寻址.另请参见下面的脚注1和this answer for syntax.只考虑使用32位绝对寻址,它实际上对代码大小有用而不是有害.例如NASM默认rel位于文件顶部.

AT& T foo(%rip)或GAS .intel_syntax noprefix使用[rip foo].

禁用PIE模式以使32位绝对寻址工作

使用gcc -fno-pie -no-pie将其重写为旧行为. -no-pie是链接器选项,-fno-pie is the code-gen option.只有-fno-pie,gcc将生成类似mov eax,offset .LC0的代码,它不与仍然启用的-pie链接.

(clang也可以默认启用PIE:使用clang -fno-pie -nopie.一个July 2017 patch -no-pie为-nopie的别名,用于与gcc compat,但是clang4.0.1没有它.)

PIE对64位(次要)或32位代码(主要)的性能成本

只有-no-pie,(但仍然是-fpie)编译器生成的代码(来自C或C源代码)会稍微慢一些,并且会比必要的更大,但仍会链接到一个位置相关的可执行文件,这将无法从中受益ASLR. “太多的PIE对性能有害”reports an average slowdown of 3% for x86-64 on SPEC CPU2006(我没有这篇论文的副本,所以IDK上的硬件是什么:/).但在32位代码中,平均减速为10%,最差情况为25%(在SPEC CPU2006上).

PIE可执行文件的代价主要用于索引静态数组,正如Agner在问题中描述的那样,使用静态地址作为32位立即数或作为[disp32 index * 4]寻址模式的一部分保存指令和寄存器vs一个RIP相对LEA,用于将地址输入寄存器.另外5字节的mov r32,imm32而不是7字节的lea r64,[rel symbol]用于将静态地址放入寄存器,这对于将字符串文字或其他静态数据的地址传递给函数来说是很好的.

-fPIE仍然假设没有全局变量/函数的符号插入,不像-fPIC用于必须通过GOT访问全局变量的共享库(这是为任何可以限制为文件范围的变量使用静态的另一个原因)全球).见The sorry state of dynamic libraries on Linux.

因此,对于64位代码,-fPIE比-fPIC要差得多,但对于32位仍然不好,因为RIP相对寻址不可用.请参阅some examples on the Godbolt compiler explorer.平均而言,-fPIE在64位代码中具有非常小的性能/代码大小的缺点.特定循环的最坏情况可能只有几个百分点.但是32位PIE会更糟糕.

这些-f code-gen选项在链接时没有任何区别,

或者在组装时.S手写的asm. gcc -fno-pie -no-pie -O3 main.c nasm_output.o是你想要两个选项的情况.

检查GCC配置

如果你的GCC是这样配置的,gcc -v |& grep -o -e'[^] * pie’打印–enable-default-pie.在0700中向gcc添加了对此配置选项的支持.Ubuntu在16.10中启用了它,而Debian在gcc 6.2.0-7中同时启用了它(导致内核生成错误:https://lkml.org/lkml/2016/10/21/904).

请注意,ld本身并未更改其默认值.它仍然可以正常工作(至少在Arch Linux上使用binutils 2.28).更改是gcc默认将-pie作为链接器选项传递,除非您明确使用-static或-no-pie.

在NASM源文件中,我使用a32 mov eax,[abs buf]来获取绝对地址. (我正在测试编码小绝对地址的6字节方式(地址大小mov eax,moffs:67 a1 40 f1 60 00)是否在Intel CPU上有LCP停顿.It does.)

nasm -felf64 -Worphan-labels -g -Fdwarf testloop.asm &&

ld -o testloop testloop.o # works: static executable

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

检查现有可执行文件是否为PIE

file和readelf说PIE是“共享对象”,而不是ELF可执行文件.静态可执行文件不能是PIE.

$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, ...

## Or with a more recent version of file:

a.out: ELF 64-bit LSB pie executable, ...

半相关(但不是真的):另一个最近的gcc功能是gcc -fno-plt.最后调用共享库可以只调用[rip symbol @ GOTPCREL](AT& T call * puts @ GOTPCREL(%rip)),没有PLT蹦床.

发行版有望很快开始启用它,因为它还避免了需要可写的可执行内存页面.对于进行大量共享库调用的程序来说,这是一个显着的加速,例如,在任何硬件the patch author tested on上,x86-64 clang -O2 -g编译tramp3d从41.6s到36.8s.(clang可能是共享库调用的最坏情况.)

它确实需要早期绑定而不是延迟动态链接,因此对于立即退出的大型程序来说速度较慢. (例如clang –version或编译hello.c).显然,通过预链接可以减少这种放缓.

但是,这不会消除共享库PIC代码中外部变量的GOT开销. (参见上面的godbolt链接).

脚注1

Linux ELF共享对象中实际允许64位绝对地址,text relocations允许在不同地址(ASLR和共享库)加载.这允许你在.rodata节中有跳转表,或者静态const int * foo =& bar;没有运行时初始值设定项.

所以mov rdi,qword msg工作(10字节mov r64, imm64的NASM / YASM语法,又名AT& T语法movabs,唯一可以使用64位立即数的指令).但这比lea rdi更大,通常更慢,[rel msg],如果你决定不禁用-pie,你应该使用它.根据Agner Fog’s microarch pdf,从Sandybridge系列CPU上的uop缓存中获取64位立即数较慢.(是的,同一个人问这个问题.:)

您可以使用NASM的默认rel而不是在每个[rel symbol]寻址模式中指定它.有关避免32位绝对寻址的更多描述,另请参见Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array. OS X根本不能使用32位地址,因此RIP相对寻址也是最佳方式.

在位置相关的代码(-no-pie)中,当你想要一个寄存器中的地址时,你应该使用mov edi,msg; 5字节的mov r32,imm32甚至比RIP相对LEA小,并且更多的执行端口可以运行它.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值