Reputation: 8627
I think I'm confused between x86-64/i686 architectures and 32-bit/64-bit operating systems.
If I build an app with g++
on a 32-bit operating system (e.g. Ubuntu 14.04) then -m64
is not a valid option and I can't build 64-bit binary. However, g++
does not complain about using -march=x86-64
. (Hardware is Core i7.)
Then, if I build on a 64-bit OS (e.g. Ubuntu 20.04), I can switch between -m32
and -m64
and build 32-bit or 64-bit binaries.
So, my understanding is...
-m32
and -m64
dictates which operating systems I can use the binaries on. (Including 32-bit binaries used on 64-bit OS with appropriate libraries)
And -march
defines which instruction set will be used for code generation - regardless of the operating system it's destined to run on.
So my primary question is...
Given that I can use -m32 -march=x86-64
for building and running a binary on a 32-bit OS, does that mean x86-64 instructions can be used on a 32-bit OS?
Upvotes: 1
Views: 1754
Reputation: 365417
No, 64-bit instructions can't be used under a 32-bit OS (or even in 32-bit user-space under a 64-bit kernel), and that's not what -march=x86-64
means.
-march=x86-64
means the target CPU supports the baseline set of ISA extensions that x86-64 guarantees: SSE2, P6 features like CMOV, CPUID, RDTSC, and so on. But without -m64
, the code will still work in 32-bit mode.
(For a target ISA of x86-64, they aren't "extensions" per-se. But we still generally say that x86-64 implies SSE and SSE2 because that's a handy way to categorize those instructions that operate on XMM registers. That makes sense if we think of x86-64 itself as an extension to x86.)
So -march=x86-64
is very similar to -march=k8
or -march=nocona
(specific early x86-64 CPUs). Just like those options, it's fully valid with -m32
and doesn't imply making 64-bit code.
GCC still knows (from -m32
) that it's targeting 32-bit mode (protected or compat mode), not 64-bit long mode.
Since those modes use incompatible machine-code formats, it's not at all like the 16-bit to 32-bit transition where you might expect -march=i386
while making 16-bit code to let the compiler use 32-bit registers. In x86-64, 64-bit registers can only be used in long mode, not compat mode or any sub-mode of legacy mode (such as protected mode). See https://en.wikipedia.org/wiki/X86-64#Operating_modes
The key point here is that there's nothing a 32-bit process can do in compat mode (under a 64-bit kernel) that it can't do in protected mode (under a 32-bit kernel). x86-64 doesn't provide a way for 32-bit processes to take advantage of running under a 64-bit kernel. (Except by making a far jmp to a 64-bit code segment, but most OSes don't support this and compilers are definitely won't emit code to do it. And of course using the full 4GB of address space, but in terms of actual machine code instructions there's nothing.) So there aren't any features you'd ever want to let a compiler use that could only work in 32-bit mode under a 64-bit kernel.
TL:DR: -m32 -march=x86-64
could be interpreted as "target compat/protected mode of a generic x86-64 CPU".
In practice it really just means "enable this set of ISA extensions", and the name isn't any more meaningful to GCC than -march=foobarbaz
or -mtune=intel
, it's just a text string for a table of tuning and ISA-extension settings.
Upvotes: 3