从64位呼叫门返回

时间:2014-02-23 03:30:45

标签: assembly x86 64-bit

在64位环0环境中,我想切换到环3,工具链:x86-64 gcc 4.6.3,为2.22。相关代码:

#define SHELL64_PHY_BASE    (12 * 1024 * 1024)
#define SHELL64_VIRT_BASE   0x10000000000
#define SHELL64_MAP_SIZE    (4 * 1024 * 1024)

// selector for ring 3 app on 64-bit OS
#define SEL_SHELL64_CODE    (0 | 4 | 3)
#define SEL_SHELL64_DATA    (8 | 4 | 3)

size_t kernel64(size_t par)
{
    map_info_t minfo;

    prints(white, "-------------------------\n");
    prints(OS_INFO "Hello, 64-bit OS!\n");

    // prepare GDT/LDT/TSS/IDT
    update_gdt();
    setup_descriptors();

    // Shell64: 64-bit shell app, map config
    minfo.vaddr = SHELL64_VIRT_BASE;
    minfo.paddr = SHELL64_PHY_BASE;
    minfo.bytes = SHELL64_MAP_SIZE;
    minfo.psize = 0x1000;
    minfo.attr = PATTR_WB | PATTR_U_RW_EX | PATTR_EXIST;
    paging_construct(&minfo);

    dcache_flush();

    sector_read((void *)SHELL64_VIRT_BASE, shell64_sec_start, shell64_sec_count);

    prints(OS_INFO "Will switch to 64-bit shell\n");

    dcache_flush();

    switch_to_shell();

    return 0;
}

void switch_to_shell(void)
{
    __asm volatile
    (
        "sub rsp, 32\n\t"
        "mov qword ptr [rsp + 24], %0\n\t"
        "mov qword ptr [rsp + 16], %1\n\t"
        "mov qword ptr [rsp + 8], %2\n\t"
        "mov qword ptr [rsp], %3\n\t"
        "mov rbp, [rsp + 16]\n\t"
        "mov ds, %4\n\t"
        "mov es, %4\n\t"
        "mov fs, %4\n\t"
        "mov gs, %4\n\t"
        "retf 0"
        : : "i"(SEL_SHELL64_DATA), "r"(SHELL64_VIRT_BASE + SHELL64_MAP_SIZE),
        "i"(SEL_SHELL64_CODE), "r"(SHELL64_VIRT_BASE), "a"(SEL_SHELL64_DATA)
    );
}

GDT信息:

+0 null desc
+8 64-bit code desc, non-conforming, DPL 0, for os
+16 data desc, DPL 0, for os
+24 LDT desc
+40 TSS desc

LDT信息:

+0 64-bit code desc, non-conforming, DPL 3, for app
+8 data desc, DPL 3, for app
+... the other desc

执行远程返回指令(retf)时,处理器输入异常。如果运行debug命令'c',则会出现大量错误并崩溃。

调试后,我发现异常向量为13,#GP fault。 输入异常时,堆栈信息:

[rsp + 0x00]:00000000 00000100 // error code
[rsp + 0x08]:ffff8000 00001b58 // old RIP
[rsp + 0x10]:00000000 00000008 // old CS
[rsp + 0x18]:00000000 00010082 // RFLAGS
[rsp + 0x20]:ffff8000 000221d0 // old RSP
[rsp + 0x28]:00000000 00000010 // old SS

bochs 2.6.2,Windows版本。主机操作系统是Win8.1

相关的崩溃信息:http://pan.baidu.com/s/1kT0qr4r

1 个答案:

答案 0 :(得分:1)

问题是as没有为REX指令生成RETF前缀。因此,您的代码以32位操作数大小执行,并且假设堆栈包含值为SHELL64_VIRT_BASE的{​​{1}},则它将被解释为32位0x10000000000 = CS:EIP。在您的问题的上一版本中,您声明0x100:0x00000000会出现以下错误:

bochs

知道那里的数字是十六进制的,00012169062e[CPU0] fetch_raw_desciptor: GDT: index (107) 20 > limit (37) 的段选择器确实会尝试访问字节0x100,并且会有索引0x107

解决方案:在intel语法中,您必须手动将前缀指定为0x20rex64 retf。使用at& t语法,您可以使用将自动生成前缀的rex.w retf助记符。