位置无关代码的区别:x86 vs x86-64
Posted
技术标签:
【中文标题】位置无关代码的区别:x86 vs x86-64【英文标题】:Difference in position-independent code: x86 vs x86-64 【发布时间】:2011-03-09 23:20:02 【问题描述】:我最近正在构建一个针对 x86-64 架构的共享库 (ELF),如下所示:
g++ -o binary.so -shared --no-undefined ... -lfoo -lbar
失败并出现以下错误:
重定位R_X86_64_32 对'a local symbol' 在制作共享对象时不能使用;用 -fPIC 重新编译
当然,这意味着我需要将它重新构建为与位置无关的代码,因此它适合链接到共享库中。
但这在 x86 上运行良好,具有完全相同的构建参数。所以问题是,x86 上的重定位与 x86-64 有何不同,为什么我不需要在前者上使用 -fPIC
进行编译?
【问题讨论】:
我从来不明白这一点。如果编译器可以准确地告诉您要自动使用哪个选项,为什么它需要您说出魔术词才能使其正确运行?呜呜呜…… @Billy ONeal,现在我相信这是泄漏抽象的情况。它们在加载全局数据的方式上有所不同,这会影响是否需要 PIC。 我理解差异化的必要性。我不明白的是为什么你需要给编译器一个开关来让它做到这一点。 @Billy,错误来自链接器 @GearoidMurphy:好的,6 个,另外 6 个。编译是通过对 g++ 的一次调用(而不是分别调用编译器和链接器)来调用的,因此 g++ 应该能够轻松判断某个编译器选项是否需要相应的链接器选项。 【参考方案1】:我找到了a nice and detailed explanation,归结为:
-
x86-64 使用 IP 相对偏移量来加载全局数据,x86-32 不能,因此它取消引用全局偏移量。
IP 相对偏移不适用于共享库,因为全局符号可以被覆盖,因此 x86-64 在不使用 PIC 构建时会崩溃。
如果 x86-64 使用 PIC 构建,则 IP 相对偏移解引用现在会产生 指向 GOT 条目的指针,然后将其解引用。
x86-32,然而,已经使用了全局偏移的解引用,所以直接变成了GOT入口。
【讨论】:
【参考方案2】:这是一个代码模型问题。默认情况下,静态代码是在假设整个程序将停留在内存地址空间的较低 2G 部分的情况下构建的。共享库的代码需要针对另一个内存模型编译,无论是 PIC,还是使用 -mcmodel=large 进行编译,而无需做出该假设。
请注意 -mcmodel=large 未在较旧的 gcc 版本中实现(它在 4.4 中,不在 4.2 中,我不知道 4.3)。 .
【讨论】:
这是有道理的——32位绝对地址不能变成相对重定位,因为库的加载地址可能> 2GB。 是的,我知道与位置无关的代码在计算跳转偏移量时需要有所不同,但我很难理解为什么它确实可以在 x86 没有-fpic
.
@Alex,动态加载器能够处理一些但不是所有的重定位记录,并且一些重定位记录没有被处理的原因是它们假设了一个不正确的情况。只有一种非 PIC 32 位内存模型,并且该模型仅使用已处理的重定位记录。有几种非 PIC 64 位内存模型,有些兼容动态重定位,有些不兼容。如果在 gcc 4.4 中使用 -mcmodel=large,则不需要 -fpic。【参考方案3】:
这纯粹是 ABI 人员强加给我们的任意要求。 x86_64 上的动态链接器不能支持非 PIC 库没有任何合乎逻辑的理由。但是,由于 x86_64 不像 x86 那样承受可怕的寄存器压力(并且具有更好的 PIC 功能),我不知道有什么重要的理由不使用 PIC。
【讨论】:
除了它确实支持非 PIC 库。它不支持某些重定位记录,因为通常 .so 以这样的方式加载,这些重定位记录所做的假设是无效的,但如果您不使用它们,则没有问题。以上是关于位置无关代码的区别:x86 vs x86-64的主要内容,如果未能解决你的问题,请参考以下文章
关于VS项目平台的x86,x64,Any CPU以及Debug和Release的区别