编译 x86_64 架构时是否应该使用 gcc -fPIC 选项?

sha*_*874 1 c linux x86 gcc position-independent-code

gcc 文档指出:“此选项在 AArch64、m68k、PowerPC 和 SPARC 上有所不同。” 我没有看到列出的 x86 或 x64 架构。AArch64 与 ARM 处理器有关,不是吗?如果针对 linux_x86_x64 进行编译,那么此选项会产生任何值吗?

Pet*_*des 7

您引用的文本来自 GCC 手册条目,-fPIC该条目直接位于 的条目-fpic之后。 当他们说“有所作为”时,他们的意思是与……相比-fpic

-fpic 如果目标机器支持,则生成适合在共享库中使用的位置无关代码 (PIC)。此类代码通过全局偏移表(GOT)访问所有常量地址。动态加载器在程序启动时解析 GOT 条目(动态加载器不是 GCC 的一部分;它是操作系统的一部分)。如果链接的可执行文件的 GOT 大小超过特定于计算机的最大大小,您会从链接器收到一条错误消息,指示 -fpic 不起作用;在这种情况下,请使用 -fPIC 重新编译。(这些最大值在 SPARC 上为 8k,在 AArch64 上为 28k,在 m68k 和 RS/6000 上为 32k。x86 没有这样的限制。)

...

-fPIC
如果目标机器支持,则发出与位置无关的代码,适合动态链接并避免对全局偏移表大小的任何限制。此选项在 AArch64、m68k、PowerPC 和 SPARC 上有所不同。

...

x86-64 无法使用有限范围的位移来保存指令或机器代码大小,因为调用/跳转和 RIP 相对寻址使用rel3232 位位移,因此它们可以到达指令的 +-2GiB 范围内的任何位置。这是“小”和“小 PIC”代码模型中允许的完整大小,基本上足以满足任何需求,除了有时.bss. (“中”和“大”对超过特定阈值的数组或使每个函数调用成为间接跳转的所有内容使用 64 位绝对地址)。

x86-64 确实有jmp rel8(但没有call rel8),但编译器只会在一个函数碰巧尾调用同一编译单元中的另一个函数时才会使用它,因此指令会汇编到同一个目标文件中,并且汇编器可以在汇编时看到距离并可以选择较短的分支编码。跳转到当前目标文件之外的代码总是会被汇编到jmp rel32,因为jmp rel8很少适合,只有 -128..+127 字节范围。(如果链接器需要更多空间,则无法使指令变得更大;这会改变其前后指令之间的距离,这可能没有重定位信息,因为汇编器假设距离是恒定的。)


如果您正在编译共享库,是的,您应该使用-fPIC. 它与 x86-64 上没有什么不同-fpic,但您需要其中之一。

尽管现代发行版将 GCC 配置-fPIE为默认值,但 x86-64 上的主要区别是我认为假设符号插入对于全局变量是可能的,而全局变量则不是__attribute__((visibility("hidden"))).

-fPIE我认为 code-gen 适合 x86-64 上的共享库,但-fno-pie-fno-pic不适合,并且在链接到 PIE 或普通共享库时会出现链接错误。(看到x86-64 Linux 中不再允许使用 32 位绝对地址了吗?