在64位x86平台上,比较PIE、PIC代码和可执行文件有什么区别?

25

这个测试运行在Ubuntu 12.04 64位的x86架构上。

我对位置无关可执行文件(PIE)和位置无关代码(PIC)的概念感到困惑,而且我猜它们不是正交的。

这是我的快速实验。

gcc -fPIC -pie quickSort.c -o a_pie.out
gcc -fPIC      quickSort.c -o a_pic.out
gcc                           a.out

objdump -Dr -j .text a.out > a1.temp
objdump -Dr -j .text a_pic.out > a2.temp
objdump -Dr -j .text a_pie.out > a3.temp

我有以下发现。

A. a.out包含一些PIC代码,但仅仅存在于libc的序言和结语函数中,如下所示:

4004d0:       48 83 3d 70 09 20 00    cmpq   $0x0,0x200970(%rip)        # 600e48 <__JCR_END__> 

在我编写的简单快速排序程序的汇编指令中,我没有找到任何PIC指令。

B. a_pic.out包含PIC代码,并且我没有找到任何非PIC指令... 在我的快速排序程序的指令中,所有全局数据都是通过像这样的PIC指令访问的:

  40053b:       48 8d 05 ea 02 00 00    lea    0x2ea(%rip),%rax        # 40082c <_IO_stdin_used+0x4>

C. a_pie.out的语法和a_pic.out相同,但是a_pie.out的.text段的内存地址范围从0x630到0xa57,而a_pic.out的相同部分范围从0x400410到0x400817。

有人能解释一下这些现象吗?特别是发现C。同时,我真的很困惑PIE vs. PIC,不知道如何解释C的发现。


3
基本上,“PIE”是用于可执行文件的,而“PIC”用于共享库。检查“PIC”可执行文件没有太多意义。还要注意,gcc会相应地调用链接器。 - Jester
-fPIC -pie /facepalm。你应该使用 -fPIE 代码生成选项,而不是 -fPIC-pie 是一个链接器选项,用于控制代码实际放入哪种可执行文件中。(-pie 单独使用会失败,因为它与32位绝对寻址不兼容)。有关 PIE 可执行文件与非 PIE 的更多信息,请参见 32-bit absolute addresses no longer allowed in x86-64 Linux?。(请注意,大多数发行版都使用 -fPIE -pie 作为默认值构建 GCC)。 - Peter Cordes
1个回答

26
我对Position Independent Executable (PIE)和Position Independent code (PIC)的概念感到困惑,我猜它们并不是正交的。 PIEPIC之间唯一真正的区别是在PIC中允许对符号进行插入,而在PIE中不允许。除此之外,它们基本上是等同的。
你可以在这里阅读有关符号插入的内容here
C. a_pie.out的语法标识指令与a_pic.out相同。然而,a_pie.out的.text部分的内存地址范围从0x630到0xa57,而a_pic.out的相同部分范围从0x400410到0x400817。

PIE二进制文件被链接为共享库,因此其默认加载地址(第一个LOAD段的.p_vaddr)为零。期望是有某种方式将该二进制文件从零页重新定位,并加载到某个随机地址。

另一方面,非PIE可执行文件始终在其链接地址处加载。在Linux上,x86_64二进制文件的默认地址为0x400000,因此.text与之相距不远。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接