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

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

问题描述

我的程序在 x86_64 CPU(64 位操作系统,ubuntu 8.04)上以 32 位模式运行.是否可以在用户模式下暂时切换到 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,

我的问题是在 32 位程序、64 位操作系统中运行 64 位代码"

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

推荐答案

与其他答案相反,我断言原则上简短的答案是YES.这可能不会以任何方式得到官方支持,但它似乎有效.在这个答案的最后,我展示了一个演示.

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 位(和 X32 也是如此,根据 GDB 来源)进程获得 CS 寄存器等于 0x23 — 32 位环的选择器3 GDT 中定义的代码段(它的基数是0).而 64 位进程得到另一个选择器:0x33 — 长模式(即 64 位)环 3 代码段的选择器(ESCS 的基础)>、SSDS 在 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天全站免登陆