为什么gcc强制PIC为x64共享库?

Eli*_*sky 10 linux x86 assembly x86-64 shared-libraries

尝试将非PIC代码编译到x64上的共享库中,gcc导致错误,例如:

/usr/bin/ld: /tmp/ccQ2ttcT.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
Run Code Online (Sandbox Code Playgroud)

这个问题是为什么会这样.我知道x64具有RIP相对寻址,旨在提高PIC代码的效率.但是,这并不意味着加载时重定位不能(理论上)应用于此类代码.

一些在线资源,包括这个(在这个问题上被广泛引用)声称,由于 RIP相对寻址,在共享库中禁止非PIC代码存在一些固有的限制.我不明白为什么这是真的.

考虑"旧x86" - call指令也有一个IP相关操作数.然而,其中的x86代码可以call很好地编译成没有PIC的共享库,但使用加载时重定位 R_386_PC32.对于x64中的数据RIP相对寻址,不能做同样的事情吗?

请注意,我完全理解PIC代码的好处,并且性能损失RIP相对寻址有助于缓解.不过,我很好奇不允许使用非PIC代码的原因.它背后是否有真正的技术推理,还是鼓励编写PIC代码?

Mat*_*Mat 16

这是我从comp.unix.programmer上的帖子中读到的最好的解释:

共享库需要x86-64上的PIC,或者更准确地说,可重定位代码必须是PIC.这是因为在重定位后,代码中使用的32位立即数地址操作数可能需要超过32位.如果发生这种情况,则无处可写新值.

  • @EliBendersky你的评论"这些天真正的限制"使*没有意义.在`CALL absolute-address`指令编码中只有32位可用.如果调用需要超过2GB(在任一方向上),则运行时加载程序没有足够的位来写入新的目标地址.并且改变x86 insturction编码是AMD在提出x86_64时试图避免的. (5认同)

Xia*_*ami 5

再说一些额外的话.

问题中提供的url中,它提到您可以传递-mcmodel=large给gcc告诉编译器为您的代码生成64位立即数地址操作数.

因此,gcc -mcmodel=large -shared a.c将生成非PIC共享对象.

-

演示:

AC:

#include <stdio.h>

void foo(void)
{
    printf("%p\n", main);
}
Run Code Online (Sandbox Code Playgroud)

32位立即数地址操作数阻止您生成非PIC对象.

xiami@gentoo ~ $ cc -shared -o a.so a.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/cck3FWeL.o: relocation R_X86_64_32 against `main' can not be used when making a shared object; recompile with -fPIC
/tmp/cck3FWeL.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

使用-mcmodel=large来解决这个问题.(警告仅出现在我的系统上,因为我的PaX内核禁止对.text进行修改.)

xiami@gentoo ~ $ cc -mcmodel=large -shared -o a.so a.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccZ3b9Xk.o: warning: relocation in readonly section `.text'.
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: warning: creating a DT_TEXTREL in object.
Run Code Online (Sandbox Code Playgroud)

现在您可以看到重定位条目的类型是R_X86_64_64,而不是R_X86_64_32,R_X86_64_PLT32,R_X86_64_PLTOFF64.

xiami@gentoo ~ $ objdump -R a.so
a.so:      file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
...
0000000000000758 R_X86_64_64       printf
...
Run Code Online (Sandbox Code Playgroud)

在我的系统上,将此共享对象链接到正常代码并运行该程序将发出如下错误: ./a.out: error while loading shared libraries: ./a.so: cannot make segment writable for relocation: Permission denied

这证明动态加载器特林做搬迁的.text其PIC库不会.