x86应用程序(使用gcc -m32构建)支持使用.code64汇编64位代码,这意味着x86应用程序可以使用64位寄存器。但是从内核方面来说,应用程序只是一个IA-32应用程序。
例如,我可以在符号64bit_test
下面链接到x86应用程序。
ENTRY(64bit_test)
.code64;
push %r8
push %r12
END(64bit_test)
当内核设置信号处理程序时,内核只保存没有64位寄存器的32位寄存器,64位寄存器上下文是否丢失?我认为这是不正确的,因为64位寄存器是使用和应该保存和恢复以后。
if (is_ia32_frame(ksig)) {
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
return ia32_setup_rt_frame(usig, ksig, cset, regs);
else
return ia32_setup_frame(usig, ksig, cset, regs);
} else if (is_x32_frame(ksig)) {
return x32_setup_rt_frame(ksig, cset, regs);
} else {
return __setup_rt_frame(ksig->sig, ksig, set, regs);
}
static int ia32_setup_sigcontext(struct sigcontext_32 __user *sc,
void __user *fpstate,
struct pt_regs *regs, unsigned int mask)
{
int err = 0;
put_user_try {
put_user_ex(get_user_seg(gs), (unsigned int __user *)&sc->gs);
put_user_ex(get_user_seg(fs), (unsigned int __user *)&sc->fs);
put_user_ex(get_user_seg(ds), (unsigned int __user *)&sc->ds);
put_user_ex(get_user_seg(es), (unsigned int __user *)&sc->es);
put_user_ex(regs->di, &sc->di);
put_user_ex(regs->si, &sc->si);
put_user_ex(regs->bp, &sc->bp);
put_user_ex(regs->sp, &sc->sp);
put_user_ex(regs->bx, &sc->bx);
put_user_ex(regs->dx, &sc->dx);
put_user_ex(regs->cx, &sc->cx);
put_user_ex(regs->ax, &sc->ax);
put_user_ex(current->thread.trap_nr, &sc->trapno);
put_user_ex(current->thread.error_code, &sc->err);
put_user_ex(regs->ip, &sc->ip);
put_user_ex(regs->cs, (unsigned int __user *)&sc->cs);
put_user_ex(regs->flags, &sc->flags);
put_user_ex(regs->sp, &sc->sp_at_signal);
put_user_ex(regs->ss, (unsigned int __user *)&sc->ss);
put_user_ex(ptr_to_compat(fpstate), &sc->fpstate);
/* non-iBCS2 extensions.. */
put_user_ex(mask, &sc->oldmask);
put_user_ex(current->thread.cr2, &sc->cr2);
} put_user_catch(err);
return err;
}
我希望64位寄存器r8
到r15
应该保存在sigcontext
中,然后再恢复,但是从代码中,r8
到r15
是缺失的。
发布于 2019-08-23 05:40:29
TL:不,这不是.code64
所做的,也没有一个Linux不支持32位进程,这个进程会跳转到64位用户空间。
.code64
只允许您将64位机器代码放入32位对象文件/可执行文件中。例如,如果您想编写一个修补64位可执行文件的32位程序,并且希望汇编程序为您生成该数据,即使它永远不会在32位程序中执行。
或者,如果您正在编写以16位或32位模式开始并切换到64位模式的自己的内核,您可以使用.code64
作为内核跳转的部分,CS指的是64位代码段。
将机器代码解码为64位而不是32位需要将CPU置于不同的模式中。x86机器代码不支持在没有模式切换的情况下混合32位和64位机器代码,没有足够的编码空间。编码非常相似,但有些操作码在64位模式(例如堆栈操作数)中具有不同的默认操作数大小,例如push %eax
和push %rax
具有相同的1字节操作码。
.code64;
; push %r8
测试实际上为 inc %eax
( REX前缀)和 push %eax
.创建了32位机器代码。是的,它装配和运行,但作为不同的指示。在layout reg
中使用GDB单步查看根据CPU所处的实际模式(而不是源模式)进行反汇编。
不同之处包括64位长模式将1字节inc/dec (0x40..4f
)操作码重新定义为REX前缀。例如x86-32 / x86-64 polyglot machine-code fragment that detects 64bit mode at run-time?
注意到这与16对32有很大的不同。16位代码可以在16位模式中使用操作数大小的前缀来访问32位寄存器和寻址模式.例如,mov eax, 1234
在.code16
(带有操作数大小的前缀)或.code32
(没有前缀)中组装得很好。
但是您不能在add rax, rdx
之外执行.code64
,因为如果不将CPU切换到不同的模式,就无法运行它。(模式由CS指向的GDT / LDT条目选择)。
理论上,您可以将用户空间中的jmpl
(far jmp)转换为用户空间进程中的不同代码段,从"compat模式“(64位内核下的32位模式)切换到完全64位模式。您必须知道要使用哪些CS值,但大多数OSes的32位和64位用户空间(CPL=3)代码段都有一些“众所周知”的常量值。
如果这听起来非常神秘和复杂,那就是我的观点。
基本上不支持进程内的模式切换(来自OS系统调用和上下文切换、动态链接器和工具链)。这通常是个糟糕的主意,别这么做。
例如,正如您注意到的那样,内核只保存/恢复在传递信号时以32位开始的进程的遗留IA32状态,因此如果它已经远跳到64位用户空间,信号处理程序将损坏高寄存器。(r8..r11在x86-64系统call中被呼叫失败).
半相关:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
https://stackoverflow.com/questions/57618695
复制相似问题