在64位linux上从32位模式切换到64位(长模式) [英] Switch from 32bit mode to 64 bit (long mode) on 64bit linux

查看:434
本文介绍了在64位linux上从32位模式切换到64位(长模式)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序以32位模式运行在x86_64 CPU(64位操作系统,ubuntu 8.04)上.是否可以在用户模式下暂时切换到64位模式(长模式)?如果可以,怎么办?

My program is in 32bit mode running on x86_64 CPU (64bit OS, ubuntu 8.04). Is it possible to switch to 64bit mode (long mode) in user mode temporarily? If so, how?

背景故事:我正在编写一个与32位模式程序链接的库,因此启动时必须为32位模式.但是,我想使用更快的x86_64指令来获得更好的性能.因此,我想切换到64位模式进行一些纯计算(无需操作系统交互;无需64位寻址),然后返回到32位,然后再返回到调用方.

Background story: I'm writing a library linked with 32bit mode program, so it must be 32bit mode at start. However, I'd like to use faster x86_64 intructions for better performance. So I want to switch to 64bit mode do some pure computation (no OS interaction; no need 64bit addressing) and come back to 32bit before returning to caller.

我发现有一些相关但不同的问题.例如,

I found there are some related but different questions. For example,

  • run 32 bit code in 64 bit program
  • run 64 bit code in 32 bit OS

我的问题是在32位程序,64位OS中运行64位代码"

My question is "run 64 bit code in 32 bit program, 64 bit OS"

推荐答案

与其他答案相反,我断言原则上简短的答案是.官方可能不会以任何方式支持此功能,但它似乎可以正常工作.在此答案的最后,我演示了一个演示.

Contrary to the other answers, I assert that in principle the short answer is YES. This is likely not supported officially in any way, but it appears to work. At the end of this answer I present a demo.

在Linux-x86_64上,一个32位(根据GDB消息来源,X32也是)进程将获得CS寄存器,该寄存器等于0x23 — GDT中定义的32位第3环代码段的选择器(其基址为0). 64位进程得到另一个选择器:0x33 —长模式(即64位)环形3代码段的选择器(ESCSSSDS的基在64位模式).因此,如果我们进行远距离跳转,远距离调用或与0x33的目标段选择器类似的操作,我们会将相应的描述符加载到CS的阴影部分,并将最终以64位段结束.

On Linux-x86_64, a 32 bit (and X32 too, according to GDB sources) process gets CS register equal to 0x23 — a selector of 32-bit ring 3 code segment defined in GDT (its base is 0). And 64 bit processes get another selector: 0x33 — a selector of long mode (i.e. 64 bit) ring 3 code segment (bases for ES, CS, SS, DS are treated unconditionally as zeros in 64 bit mode). Thus if we do far jump, far call or something similar with target segment selector of 0x33, we'll load the corresponding descriptor to the shadow part of CS and will end up in a 64 bit segment.

此答案底部的演示使用jmp far指令跳转到64位代码.请注意,我选择了一个特殊的常量加载到rax中,以便对于32位代码,该指令看起来像

The demo at the bottom of this answer uses jmp far instruction to jump to 64 bit code. Note that I've chosen a special constant to load into rax, so that for 32 bit code that instruction looks like

dec eax
mov eax, 0xfafafafa
ud2
cli ; these two are unnecessary, but leaving them here for fun :)
hlt

如果我们在CS影子部分中执行具有32位描述符的描述符,则必须失败(将在ud2指令上引发SIGILL).

This must fail if we execute it having 32 bit descriptor in CS shadow part (will raise SIGILL on ud2 instruction).

现在是演示(使用fasm进行编译).

Now here's the demo (compile it with 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 "Kernel appears to be 32 bit, can't jump to long mode segment",10
msgLen = $-msg

这篇关于在64位linux上从32位模式切换到64位(长模式)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆