使用 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)?