我的程序是在x86_64 CPU(64位操作系统,Ubuntu 8.04)上运行的32位模式。 是否可以暂时在用户模式下切换到64位模式(长时间模式)? 如果是这样,怎么样?
背景故事:我正在写一个链接到32位模式程序的库,所以它在启动时必须是32位模式。 但是,我想使用更快的x86_64入侵以获得更好的性能。 所以我想切换到64位模式做一些纯粹的计算(没有操作系统的交互;不需要64位寻址),返回到32位,然后返回给调用者。
我发现有一些相关但不同的问题。 例如,
我的问题是“在32位程序,64位操作系统中运行64位代码”
与其他答案相反,我断言原则上简短答案是肯定的 。 这可能不是以任何方式正式支持,但它似乎工作。 在这个答案的结尾,我提出了一个演示。
在Linux-x86_64上,一个32位(根据GDB源的X32)进程获得的CS
寄存器等于0x23
GDT中定义的32位环3代码段的选择符(其基数为0
)。 并且64位进程得到另一个选择器: 0x33
长模式(即64位)环3代码段( ES
, CS
, SS
, DS
的基础在64位模式中被无条件地视为零)的选择器。 因此,如果我们做远程跳转,远程调用或类似于目标段选择器0x33
,我们将相应的描述符加载到CS
的阴影部分,最终将在64位段。
这个答案底部的演示使用jmp far
指令跳转到64位代码。 请注意,我选择了一个特殊的常量加载到rax
,以便指令看起来像是32位代码
dec eax mov eax, 0xfafafafa ud2 cli ; these two are unnecessary, but leaving them here for fun :) hlt
如果我们在CS阴影部分执行32位描述符(这会在ud2
指令中引发SIGILL), ud2
这个必须失败。
现在这里是演示(用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 ; 32 bit process on 64 bit kernel has this selector in CS jne kernelIs32Bit jmp 0x33:start64 ; switch to 64-bit segment start64: use64 mov rax, qword 0xf4fa0b0ffafafafa ; would crash inside this if executed as 32 bit code 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 "coreel appears to be 32 bit, can't jump to long mode segment",10 msgLen = $-msg
答案是不。 仅仅因为你正在运行64bit code
(大概是64位长度的数据类型,例如变量等),你并没有在32位的机器上以64位模式运行。 编译器可以在32位机器上提供64位数据类型。 例如,gcc的unsigned long long和uin64_t都是x86和x86_64机器上的8位数据类型。 出于这个原因,数据类型可以在x86和x86_64之间移植。 这并不意味着你得到一个32位的盒子上的64位地址空间。 这意味着编译器可以处理64位数据类型。 您将遇到无法在32位盒上运行一些64位代码的情况。 在这种情况下,您将需要预处理器指令来编译x86_64上正确的64位代码和x86上正确的32位代码。 一个简单的例子是明确需要不同数据类型的地方。 在这种情况下,您可以提供预处理器检查以确定主机是64位还是32位:
#if defined(__LP64__) || defined(_LP64) # define BUILD_64 1 #endif
然后您可以使用以下条件来提供条件来编译正确的代码:
#ifdef BUILD_64 printf (" x : %ld, hex: %lx,\nfmtbinstr_64 (d, 4, \"-\"): %s\n", d, d, fmtbinstr_64 (d, 4, "-")); #else printf (" x : %lld, hex: %llx,\nfmtbinstr_64 (d, 4, \"-\"): %s\n", d, d, fmtbinstr_64 (d, 4, "-")); #endif
希望这为您提供了一个起点。 如果您有更具体的问题,请发布更多详细信息。