与其他答案相反,我认为原则上短答案是“是”。这可能没有得到任何官方支持,但它似乎可以工作。在本回答结束时,我会展示一个演示。
在Linux-x86_64上,32位进程(包括X32,根据GDB源代码)将
CS
寄存器设置为
0x23
——GDT中定义的32位ring 3代码段选择器(其基址为
0
)。64位进程会获得另一个选择器:
0x33
——长模式(即64位)ring 3代码段选择器(在64位模式下,对于
ES
、
CS
、
SS
、
DS
,基地址无条件地视为零)。因此,如果我们使用目标段选择器
0x33
进行远跳转、远调用或类似操作,我们将把相应的描述符加载到
CS
的影子部分,并最终进入64位段。
本回答底部的演示使用
jmp far
指令跳转到64位代码。请注意,我选择了一个特殊的常数加载到
rax
中,以便对于32位代码,该指令看起来像。
dec eax
mov eax, 0xfafafafa
ud2
cli
hlt
如果在CS阴影部分有32位描述符执行此操作,它必将失败(ud2
指令将引发SIGILL)。
现在这里是演示(使用fasm编译它)。
format ELF executable
segment readable executable
SYS_EXIT_32BIT=1
SYS_EXIT_64BIT=60
SYS_WRITE=4
STDERR=2
entry $
mov ax,cs
cmp ax,0x23
jne kernelIs32Bit
jmp 0x33:start64
start64:
use64
mov rax, qword 0xf4fa0b0ffafafafa
xor rdi,rdi
mov eax, SYS_EXIT_64BIT
syscall
ud2
use32
kernelIs32Bit:
mov edx, msgLen
mov ecx, msg
mov ebx, STDERR
mov eax, SYS_WRITE
int 0x80
dec ebx
mov eax, SYS_EXIT_32BIT
int 0x80
msg:
db "Kernel appears to be 32 bit, can't jump to long mode segment",10
msgLen = $-msg