x86-64 Linux中不再允许32位绝对地址?
64位Linux默认使用小内存模式,这将使所有的代码和静态数据低于2GB的地址限制。 这确保您可以使用32位绝对地址。 老版本的gcc使用静态数组的32位绝对地址来保存一个额外的相对地址计算指令。 但是,这不再起作用。 如果我试图在程序集中创build一个32位的绝对地址,我得到链接器错误:“在创build共享对象时不能使用对`.data的重定位R_X86_64_32S;使用-fPIC重编译”。 这个错误信息当然是误导性的,因为我没有创build一个共享对象,-fPIC也没有帮助。 到目前为止我发现的是:gcc版本4.8.5使用静态数组的32位绝对地址,gcc版本6.3.0不使用。 版本5可能不会。 binutils 2.24中的链接器允许32位绝对地址,而2.28版本则不允许。
这种改变的后果是,旧的库必须重新编译,旧的汇编代码被破坏。
现在我想问一下:这个改变是什么时候发生的? 有logging吗? 有没有一个链接器选项,使其接受32位绝对地址?
你的发行版configuration了gcc和--enable-default-pie
,所以--enable-default-pie
,它使位置无关的可执行文件(允许ASLR的可执行文件以及库)。 PIC / PIE不允许搬迁。
使用gcc -fno-pie -no-pie
来覆盖这个旧行为。 -no-pie
是链接器选项, -fno-pie
是代码选项 。 如果只有-fno-pie
,gcc将会使代码像mov eax, offset .LC0
那样与静止的-pie
没有链接。 只有-no-pie
,编译器生成的代码会比所需的速度慢 ,但是仍然会被链接到一个依赖于位置的可执行文件,而这个可执行文件不会受益于ASLR。 惩罚主要是在访问全局variables(而不是静态存储类的static
文件范围variables)时。 请注意, -fPIE
不如-fPIC
差。 查看Godbolt编译器资源pipe理器中的一些示例 ,并查看Linux上dynamic库的抱歉状态 。
如果你的gcc是这样configuration的, gcc -v |& grep -o -e '[^ ]*pie'
打印--enable-default-pie
。 在2015年初 ,gcc增加了对这个configuration选项的支持。 Ubuntu在16.10和Debian 6.2.0-7
在同一时间在gcc 6.2.0-7
启用它(导致内核生成错误: https : 6.2.0-7
)。 相关:将压缩的x86内核构build为PIE 。
请注意, ld
本身并没有改变它的默认值。 它仍然正常工作(至less在Arch Linux上使用binutils 2.28)。 改变是gcc
默认将-pie
作为链接器选项来传递,除非你明确地使用了-static
或者-no-pie
。
在NASM源文件中,我使用了a32 mov eax, [abs buf]
来获取绝对地址。 (我正在testing编码小绝对地址(地址大小+ mov eax,moffs: 67 a1 40 f1 60 00
)的6字节方式在Intel CPU上是否有LCP失速。
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
相关: 使用/不使用libc构build静态/dynamic可执行文件,定义_start
或main
。
file
和readelf
说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, ...
半相关:另一个最近的gccfunction是gcc -fno-plt
。 最后,调用共享库可以call [rip + symbol@GOTPCREL]
(AT&T call *puts@GOTPCREL(%rip)
),而不需要PLT蹦床。
Distros希望能尽快启用它,因为它也避免了需要可写+可执行的内存页面。 这对于编写大量共享库调用的程序来说是一个显着的提速,例如x86-64 clang -O2 -g
编译tramp3d的时间从41.6s到36.8s。 (铿锵也许是共享库调用的最坏情况。)
它确实需要提前绑定而不是懒惰的dynamic链接,所以对于立即退出的大型程序来说,速度会变慢。 (例如, clang --version
或者编译hello.c
)。 显然,这种放缓可以通过预先链接来降低。
不过,这并不能消除PIC代码中的外部variables的GOT开销。 (见上面的godbolt链接)。