使用 g++ -march=x86-64 构建的代码可以在 32 位操作系统上运行吗?

Posted

技术标签:

【中文标题】使用 g++ -march=x86-64 构建的代码可以在 32 位操作系统上运行吗?【英文标题】:Can code built with g++ -march=x86-64 run on a 32-bit Operating System? 【发布时间】:2021-04-23 17:41:11 【问题描述】:

我想我在 x86-64/i686 架构和 32 位/64 位操作系统之间感到困惑。

如果我在 32 位操作系统(例如 Ubuntu 14.04)上使用 g++ 构建应用程序,那么 -m64 不是有效选项,我无法构建 64 位二进制文​​件。但是,g++ 并没有抱怨使用-march=x86-64。 (硬件是 Core i7。)

然后,如果我在 64 位操作系统(例如 Ubuntu 20.04)上构建,我可以在 -m32-m64 之间切换并构建 32 位或 64 位二进制文​​件。

所以,我的理解是……

-m32-m64 规定了我可以在哪些操作系统上使用二进制文件。 (包括在 64 位操作系统上使用的 32 位二进制文​​件和适当的库)

-march 定义了将用于代码生成的指令集——不管它注定要在什么操作系统上运行。

所以我的主要问题是......

鉴于我可以使用 -m32 -march=x86-64 在 32 位操作系统上构建和运行二进制文件,这是否意味着 x86-64 指令可以在 32 位操作系统上使用?

【问题讨论】:

【参考方案1】:

不,64 位指令不能在 32 位操作系统下使用(甚至在 64 位内核下的 32 位用户空间),这不是 -march=x86-64 的意思。


-march=x86-64 表示目标 CPU 支持 x86-64 保证的 ISA 扩展的基线集:SSE2、CMOV、CPUID、RDTSC 等 P6 特性。但是没有-m64,代码仍然可以在32位模式下工作。

(对于 x86-64 的目标 ISA,它们本身并不是“扩展”。但我们通常仍然说 x86-64 意味着 SSE 和 SSE2,因为这是对那些在 XMM 上运行的指令进行分类的便捷方法寄存器。如果我们将 x86-64 本身视为 x86 的扩展,那是有道理的。)

所以-march=x86-64-march=k8-march=nocona(特定早期x86-64 CPU)非常相似。就像这些选项一样,它对-m32 完全有效,并不意味着制作 64 位代码。 GCC 仍然知道(来自-m32)它的目标是 32 位模式(保护或兼容模式),而不是 64 位长模式。

由于这些模式使用不兼容的机器代码格式,它根本不像 16 位到 32 位的转换,您可能期望 -march=i386 在制作 16 位代码时让编译器使用 32 位寄存器。在 x86-64 中,64 位寄存器只能用于长模式,不能用于兼容模式或传统模式的任何子模式(例如保护模式)。见https://en.wikipedia.org/wiki/X86-64#Operating_modes

这里的关键点是 32 位进程在兼容模式下(在 64 位内核下)没有什么是在保护模式下(在 32 位内核下)做不到的。 x86-64 没有为 32 位进程提供在 64 位内核下运行的方法。 (除了对 64 位代码段进行远 jmp 操作,但大多数操作系统不支持此操作,编译器肯定不会发出代码来执行此操作。当然,使用完整的 4GB 地址空间,但在条款实际的机器代码指令,什么都没有。)所以没有任何你想让编译器使用的功能只能在 64 位内核下的 32 位模式下工作。


TL:DR: -m32 -march=x86-64 可以解释为“通用 x86-64 CPU 的目标兼容/保护模式”。

实际上它只是意味着“启用这组 ISA 扩展”,并且 这个名称对 GCC 来说并没有比 -march=foobarbaz-mtune=intel 更有意义,它只是一个用于表的文本字符串调整和 ISA 扩展设置。

【讨论】:

以上是关于使用 g++ -march=x86-64 构建的代码可以在 32 位操作系统上运行吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何知道在构建中使用哪个 cpu(x86 x64 或 AnyCpu)?

汇编语言-X86处理器架构-64位x86处理器

为啥 x86-64 Linux 系统调用使用 6 个寄存器集?

如何在 x86-64 汇编中使用堆栈?

lahf 啥时候可以在 x86-64 模式下使用?

x86-64指令系统