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可执行文件,定义_startmain


filereadelf说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链接)。