Switch from 32bit mode to 64 bit (long mode) on 64

2020-07-14 05:35发布

问题:

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?

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

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

回答1:

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.

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.

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

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

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


回答2:

The answer is NO. Just because you are running 64bit code (presumably 64bit length datatypes, eg. variables, etc.) you are not running in 64bit mode on a 32 bit box. Compilers have workarounds to provide 64bit data types on 32 bit machines. For example gcc has unsigned long long and uin64_t that are 8 bit datatypes on both x86 and x86_64 machines. Datatypes are portable between x86 & x86_64 for that reason. That does not mean you get 64bit address space on a 32bit box. It means the compiler can handle 64bit datatypes. You will run into instances where you cannot run some 64bit code on 32 bit boxes. In that case, you will need preprocessor instructions to compile the correct 64bit code on x86_64 and the correct 32bit code on x86. A simple example is where different datatypes are explicitly required. In that case you can provide a preprocessor check to determine if the host computer is 64bit or 32bit with:

#if defined(__LP64__) || defined(_LP64)
# define BUILD_64   1
#endif

You can then provide conditionals to compile the correct code with the following:

#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

Hopefully this provides a starting point for you to work with. If you have more specific questions, please post more details.