是否可以在 64 位 Linux 的同一可执行文件中同时使用 64 位和 32 位指令?
Is it possible to use both 64 bit and 32 bit instructions in the same executable in 64 bit Linux?
A 64位CPU(amd64)在兼容模式下支持32位Intel指令。此外,如果 ELF header 表示它是 32 位可执行文件,则 64 位 Linux 安装允许 运行 包含 32 位指令的 ELF。
我想知道是否可以在 ELF 中放入一些汇编指令,从而在程序中间将 CPU 切换为 32 位兼容模式(然后稍后再返回)?如果内核不允许这些汇编指令,是否有某种方法可以让内核将已经 运行 的进程切换到 32 位?
这主要是出于好奇而提出的问题,因为我真的看不到任何 use-cases。
长模式和兼容模式的切换是通过改变CS来完成的。用户模式代码不能修改描述符table,但它可以对描述符table中已经存在的代码段执行远跳转或远调用。我认为在 Linux(例如)中存在所需的兼容模式描述符。
这是 Linux (Ubuntu) 的示例代码。使用
构建
$ gcc -no-pie switch_mode.c switch_cs.s
switch_mode.c:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
extern bool switch_cs(int cs, bool (*f)());
extern bool check_mode();
int main(int argc, char **argv)
{
int cs = 0x23;
if (argc > 1)
cs = strtoull(argv[1], 0, 16);
printf("switch to CS=%02x\n", cs);
bool r = switch_cs(cs, check_mode);
if (r)
printf("cs=%02x: 64-bit mode\n", cs);
else
printf("cs=%02x: 32-bit mode\n", cs);
return 0;
}
switch_cs.s:
.intel_syntax noprefix
.code64
.text
.globl switch_cs
switch_cs:
push rbx
push rbp
mov rbp, rsp
sub rsp, 0x18
mov rbx, rsp
movq [rbx], offset .L1
mov [rbx+4], edi
// Before the lcall, switch to a stack below 4GB.
// This assumes that the data segment is below 4GB.
mov rsp, offset stack+0xf0
lcall [rbx]
// restore rsp to the original stack
leave
pop rbx
ret
.code32
.L1:
call esi
lret
.code64
.globl check_mode
// returns false for 32-bit mode; true for 64-bit mode
check_mode:
xor eax, eax
// In 32-bit mode, this instruction is executed as
// inc eax; test eax, eax
test rax, rax
setz al
ret
.data
.align 16
stack: .space 0x100
A 64位CPU(amd64)在兼容模式下支持32位Intel指令。此外,如果 ELF header 表示它是 32 位可执行文件,则 64 位 Linux 安装允许 运行 包含 32 位指令的 ELF。
我想知道是否可以在 ELF 中放入一些汇编指令,从而在程序中间将 CPU 切换为 32 位兼容模式(然后稍后再返回)?如果内核不允许这些汇编指令,是否有某种方法可以让内核将已经 运行 的进程切换到 32 位?
这主要是出于好奇而提出的问题,因为我真的看不到任何 use-cases。
长模式和兼容模式的切换是通过改变CS来完成的。用户模式代码不能修改描述符table,但它可以对描述符table中已经存在的代码段执行远跳转或远调用。我认为在 Linux(例如)中存在所需的兼容模式描述符。
这是 Linux (Ubuntu) 的示例代码。使用
构建$ gcc -no-pie switch_mode.c switch_cs.s
switch_mode.c:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
extern bool switch_cs(int cs, bool (*f)());
extern bool check_mode();
int main(int argc, char **argv)
{
int cs = 0x23;
if (argc > 1)
cs = strtoull(argv[1], 0, 16);
printf("switch to CS=%02x\n", cs);
bool r = switch_cs(cs, check_mode);
if (r)
printf("cs=%02x: 64-bit mode\n", cs);
else
printf("cs=%02x: 32-bit mode\n", cs);
return 0;
}
switch_cs.s:
.intel_syntax noprefix
.code64
.text
.globl switch_cs
switch_cs:
push rbx
push rbp
mov rbp, rsp
sub rsp, 0x18
mov rbx, rsp
movq [rbx], offset .L1
mov [rbx+4], edi
// Before the lcall, switch to a stack below 4GB.
// This assumes that the data segment is below 4GB.
mov rsp, offset stack+0xf0
lcall [rbx]
// restore rsp to the original stack
leave
pop rbx
ret
.code32
.L1:
call esi
lret
.code64
.globl check_mode
// returns false for 32-bit mode; true for 64-bit mode
check_mode:
xor eax, eax
// In 32-bit mode, this instruction is executed as
// inc eax; test eax, eax
test rax, rax
setz al
ret
.data
.align 16
stack: .space 0x100