64位内核是否不支持使用.code64汇编程序的IA-32应用程序? [英] Doesn't 64-bit kernel support IA-32 application with .code64 assembler?

查看:46
本文介绍了64位内核是否不支持使用.code64汇编程序的IA-32应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

x86应用程序(使用gcc -m32构建)支持具有 .code64 的Assembly 64位代码,这意味着x86应用程序可以使用64位寄存器.但是从内核方面来说,该应用程序只是一个IA-32应用程序.

x86 application(build with gcc -m32) supports Assembly 64-bit code with .code64, this means that an x86 application could use a 64-bit register. But from kernel side, the application just a IA-32 application.

例如,我可以在符号 64bit_test 下面链接到x86应用程序.

For example, I can link below symbol 64bit_test to an x86 application.

ENTRY(64bit_test)
    .code64;
    push %r8
    push %r12
END(64bit_test)

在内核设置信号处理程序中,内核仅保存32位寄存器而没有64位寄存器,是否缺少64位寄存器上下文?我认为这是不正确的,因为使用了64位寄存器,以后应将其保存和恢复.

When the kernel setup signal handler, the kernel only saved 32-bit registers without 64-bit registers, does 64-bit register context missing? I think this is incorrect because 64-bit register is used and should be saved and restored later.

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 丢失.

I expect 64-bit registers r8 to r15 should be saved in sigcontext and later restored, but from the code, r8 to r15 is missing.

推荐答案

TL:DR:不,那不是 .code64 所要做的,没有Linux 没有支持可跳至64位用户空间的32位进程.

TL:DR: no, that's not what .code64 does, and no Linux doesn't support 32-bit processes that far-jump to 64-bit user-space.

.code64 仅允许您将64位机器代码放入32位目标文件/可执行文件中.例如如果您想编写一个修补64位可执行文件的32位程序,并且希望汇编器为您生成该数据,即使它永远不会在32位程序中执行.

.code64 just lets you put 64-bit machine code inside a 32-bit object file / executable. e.g. if you wanted to write a 32-bit program that patched a 64-bit executable, and wanted to have the assembler generate that data for you even though it would never be executed inside the 32-bit program.

或者,如果您正在编写以16或32位模式开始并切换到64位模式的内核,则对内核跳转的部分使用 .code64 CS指的是64位代码段.

Or if you were writing your own kernel that started in 16 or 32-bit mode, and switched to 64-bit mode, you'd use .code64 for the part that your kernel jumps with CS referring to a 64-bit code segment.

将机器代码解码为64位而不是32位需要将CPU置于不同的模式. x86机器代码不支持在没有模式切换的情况下混合使用32位和64位机器代码.编码非常相似,但有些操作码在64位模式下具有不同的默认操作数大小(例如堆栈操作),例如 push%eax push%rax 具有相同的1字节操作码.

Decoding machine code as 64-bit instead of 32-bit requires putting the CPU in a different mode. x86 machine code does not support mixing 32 and 64-bit machine code without a mode-switch. There isn't enough coding space left for that. The encodings are very similar but with some opcodes having a different default operand-size in 64-bit mode (e.g. stack ops) e.g. push %eax and push %rax have the same 1-byte opcode.

您的 .code64; push%r8 测试实际上为 inc%eax (REX前缀)和 push%eax 创建了32位机器代码.是的,它可以组装和运行,但按照不同的说明.在 layout reg 中与GDB一起单步执行,以根据CPU所处的实际模式而非源来查看反汇编.

Your .code64; ; push %r8 test actually creates 32-bit machine code for inc %eax (the REX prefix) and push %eax. Yes it assembles and runs, but as different instructions. Single-step it with GDB in layout reg to see disassembly according to the actual mode the CPU is in, not source.

差异包括64位长模式,将1字节的inc/dec( 0x40..4f )操作码用作REX前缀.例如 x86-在运行时检测到64位模式的32/x86-64多语言机器代码片段?

The differences include 64-bit long mode repurposing 1-byte inc/dec (0x40..4f) opcodes as REX prefixes. e.g. x86-32 / x86-64 polyglot machine-code fragment that detects 64bit mode at run-time?

请注意,这与16 vs. 32完全不同.16位代码可以在16位模式下使用操作数大小的前缀 来访问32位寄存器和寻址模式.例如 mov eax,1234 可以很好地在 .code16 (带有操作数大小的前缀)或 .code32 (不带前缀)中进行组装.

Note that this is very different from 16 vs. 32. 16-bit code can use an operand-size prefix in 16-bit mode to access 32-bit registers and addressing modes. e.g. mov eax, 1234 assembles just fine in .code16 (with an operand-size prefix), or in .code32 (without a prefix).

但是您不能在 .code64 之外添加rax,rdx ,因为如果不将CPU切换到不同的模式.(模式由CS指向的GDT/LDT条目进行选择.)

But you can't do add rax, rdx outside of .code64 because there's no way to run it without switching the CPU to a different mode. (Modes are selected by the GDT / LDT entry that CS is pointing to).

理论上,您可以将用户空间中的 jmpl (远jmp)更改为用户空间过程中的其他代码段,以从兼容模式"切换为(在64位内核下为32位模式)到完全64位模式.您必须知道要使用什么CS值,但是大多数OS都有一些众所周知的" CS值.它们的32位和64位用户空间(CPL = 3)代码段的常量值.

You can in theory jmpl (far jmp) in user-space to a different code segment in your user-space process to switch from "compat mode" (32-bit mode under a 64-bit kernel) to full 64-bit mode. You'd have to know what CS value to use, but most OSes have some "well-known" constant values for their 32-bit and 64-bit user-space (CPL=3) code segments.

如果这听起来令人难以置信且复杂,是的,这就是我的意思.

If that sounds incredibly arcane and complex, yeah that's my point.

基本上(从OS系统调用和上下文切换,动态链接器和工具链)对流程内部的模式的支持为零.通常这是一个可怕的主意,不要这样做.

例如正如您所注意到的,内核仅为传递信号时以32位开始的进程保存/恢复旧版IA32状态,因此,如果该进程已跳到64位用户空间,则信号处理程序将破坏高位寄存器.(在x86-64 System V ABI中,r8..r11被呼叫了).

e.g. as you noticed, the kernel only saves / restores the legacy IA32 state for a process that started as 32-bit when delivering a signal, so if it had far-jumped to 64-bit user-space the signal handler would corrupt high registers. (r8..r11 are call-clobbered in the x86-64 System V ABI).

半相关: 查看全文

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