In x86, the 32bit and 64bit instruction encodings are mostly identical.
The big exception to that are the 16 single-byte INC
and DEC
instruction opcodes. These 16 bytes, in 64bit mode, have been repurposed into the REX
prefix family, which allows to specify 64bit operand size as well as the usage of the new registers in 64bit mode.
This means 64bit code like:
xorl %eax, %eax
.byte 0x48, 0xff, 0xc8
; this is the same as:
; decq %rax ; opcode: 0x48 0xff 0xc8
lret $0
is valid 32bit code but will there be executed as:
xorl %eax, %eax
decl %eax ; opcode: 0x48
decl %eax ; opcode: 0xff 0xc8
lret $0
So you can ljmp
to this piece of code, and test the (32bit) return value; it'll be -1
if executed in 64bit mode but -2
if executed in 32bit mode.
I do not know what the preconditions for far returns from 32bit to 64bit mode are. I suspect you might have to set up both a "low mem" 64bit stack pointer to start with as well as a low-mem 64bit code address "trampoline" (so that both the return EIP and return ESP in the far call frame are 32bit values).