加载 GDT 时自定义操作系统崩溃

问题描述 投票:0回答:1

我正在用 C++ 构建一个简单的操作系统来了解该主题。我正在使用 Limine 引导加载程序(Limine 6.20231210.0,如引导加载程序版本请求所述)。我在主内核函数中要做的第一件事如下:

    extern "C" void _start(void) {
        // Ensure the bootloader actually understands our base revision (see spec) and fetch first fb
        if(LIMINE_BASE_REVISION_SUPPORTED == false) {
            hcf();
        }
        struct limine_framebuffer* framebuffer = framebuffer_request.response->framebuffers[0];
        if(framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
            hcf();
        }

        KERNEL::VGA::VGA_Driver           vgaDriver(framebuffer);
        KERNEL::TERMINAL::kernel_terminal terminal(&terfont7x14, &vgaDriver);
        terminal.printf("%s %s", bootloader_info_request.response->name,    bootloader_info_request.response->version);
        gdt_init();
        hcf();
    }

加载GDT。如您所见,调用了 gdt_init() 函数(代码如下)。 vgaDriver 和终端对象是简单的对象,允许我写入屏幕(它们使用限制帧缓冲区)。另外.h内的gdt_init()声明被标记为ar extern“C”,所以它不能是关于函数名称的,编译器会给我一个错误

bits 64

align 0x10
gdt:
null_descriptor:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 00000000b
    db 00000000b
    db 0x00

kernel_code_64:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 10011010b
    db 00100000b
    db 0x00

kernel_data_64:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 10010010b
    db 00100000b
    db 0x00

user_code_64:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 11111010b
    db 00100000b
    db 0x00

user_data_64:
    dw 0x0000
    dw 0x0000
    db 0x00
    db 11110010b
    db 00100000b
    db 0x00

gdt_end:

gdt_ptr:
dw gdt_end - gdt - 1
dq gdt

CODE_SEG equ kernel_code_64 - gdt
DATA_SEG equ kernel_data_64 - gdt

global gdt_init
gdt_init:
    lgdt [gdt_ptr]
    mov rax, rsp
    push DATA_SEG
    push rax
    pushfq
    push CODE_SEG
    push flush
    iretq
    flush:
        mov ax, DATA_SEG
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax
        mov ss, ax
        ret

我什至尝试了不同版本的 gdt_load(),如下所示:

lgdt [rdi]  ; i was passing the parameters by function here
push 0x8   ; the code segment offset
lea rax, [test]  ; i declare the test label later in the code, after crash point
push rax
iretq
; fails after iretq

但是代码总是在 iretq 指令处失败。我尝试使用 gdb 进行调试,但据我所知,寄存器似乎一切正常。即使在 C++ 实现中,gdt 条目始终与第一个代码示例中显示的条目类似,因此不可能是这样(除非条目本身就是问题)。 我尝试构建操作系统的平台是 x86_64,这是我的 linker.ld 文件:

/* Tell the linker that we want an x86_64 ELF64 output file */
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
 
/* We want the symbol _start to be our entry point */
ENTRY(_start)
 
/* Define the program headers we want so the bootloader gives us the right */
/* MMU permissions */
PHDRS
{
    text    PT_LOAD    FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
    rodata  PT_LOAD    FLAGS((1 << 2)) ;            /* Read only */
    data    PT_LOAD    FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */
    dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */
}
 
SECTIONS
{
    /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */
    /* and because that is what the Limine spec mandates. */
    /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
    /* that is the beginning of the region. */
    . = 0xffffffff80000000;
 
    .text : {
        *(.text .text.*)
    } :text
 
    /* Move to the next memory page for .rodata */
    . += CONSTANT(MAXPAGESIZE);
 
    .rodata : {
        *(.rodata .rodata.*)
    } :rodata
 
    /* Move to the next memory page for .data */
    . += CONSTANT(MAXPAGESIZE);
 
    .data : {
        *(.data .data.*)
    } :data
 
    /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */
    .dynamic : {
        *(.dynamic)
    } :data :dynamic
 
    /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
    /* unnecessary zeros will be written to the binary. */
    /* If you need, for example, .init_array and .fini_array, those should be placed */
    /* above this. */
    .bss : {
        *(.bss .bss.*)
        *(COMMON)
    } :data
 
    /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */
    /DISCARD/ : {
        *(.eh_frame)
        *(.note .note.*)
        *(.comment)
    }
}

如你所见,我的代码主要基于 osdev 上的限制基础

这也是我的构建命令(替换 .c 和 .o 名称)

g++ -o build/memory.o -c -std=c++17 -Wall -Wno-missing-field-initializers -Wextra -Wno-switch-bool -fno-rtti -fno-stack-protector -fno-stack-check -fno-lto -m64 -march=x86-64 -g src/memory.cpp
nasm -f elf64 -o build/x86.o src/x86.asm
ld -o build/kernel -m elf_x86_64 -static --no-dynamic-linker -T src/linker.ld build/kernel.o build/memory.o build/ports.o build/terminal.o build/vga.o build/gdt.o build/x86.o

代码似乎能够在寄存器中加载GDTD的地址,因为我曾经尝试删除段更新并仅加载gdt并且它有效,并且我可以使用sgdt获取地址。返回的地址可以转换为有效的 GDT 结构(与汇编示例中的实现相同,但在 C++ 中)

c++ assembly x86-64 osdev gdt
1个回答
0
投票

这是 gdt_init() 函数的反汇编(直到 iretq)

│    0xffffffff80001608 <kernel_code_64>     add    BYTE PTR [rax],al                              │
│    0xffffffff8000160a <kernel_code_64+2>   add    BYTE PTR [rax],al                              │
│    0xffffffff8000160c <kernel_code_64+4>   add    BYTE PTR [rdx+0x20],bl                         │
│    0xffffffff80001612 <kernel_data_64+2>   add    BYTE PTR [rax],al                              │
│    0xffffffff80001614 <kernel_data_64+4>   add    BYTE PTR [rdx+0x20],dl                         │
│    0xffffffff8000161a <user_code_64+2>     add    BYTE PTR [rax],al                              │
│    0xffffffff8000161c <user_code_64+4>     add    dl,bh                                          │
│    0xffffffff8000161e <user_code_64+6>     and    BYTE PTR [rax],al                              │
│    0xffffffff80001620 <user_data_64>       add    BYTE PTR [rax],al                              │
│    0xffffffff80001622 <user_data_64+2>     add    BYTE PTR [rax],al                              │
│    0xffffffff80001624 <user_data_64+4>     add    dl,dh                                          │
│    0xffffffff80001626 <user_data_64+6>     and    BYTE PTR [rax],al                              │
│    0xffffffff80001628 <gdt_ptr>            (bad)                                                 │
│    0xffffffff80001629 <gdt_ptr+1>          add    BYTE PTR [rax],al                              │
│    0xffffffff8000162b <gdt_ptr+3>          (bad)                                                 │
│    0xffffffff8000162c <gdt_ptr+4>          add    BYTE PTR [rax-0x1],al                          │
│  > 0xffffffff80001632 <gdt_init>           lgdt   ds:0xffffffff80001628                          │
│    0xffffffff8000163a <gdt_init+8>         mov    rax,rsp                                        │
│    0xffffffff8000163d <gdt_init+11>        push   0x10                                           │
│    0xffffffff8000163f <gdt_init+13>        push   rax                                            │
│    0xffffffff80001640 <gdt_init+14>        pushf                                                 │
│    0xffffffff80001641 <gdt_init+15>        push   0x8                                            │
│    0xffffffff80001643 <gdt_init+17>        push   0xffffffff8000164a                             │
│    0xffffffff80001648 <gdt_init+22>        iretq

这些是 iretq 指令之前的寄存器状态:

rax            0xffff800007e9ff48  -140737355579576
rbx            0x0                 0
rcx            0xffff8000fd000000  -140733243719680
rdx            0x0                 0
rsi            0x0                 0
rdi            0xffff800007e9ffb0  -140737355579472
rbp            0xffff800007e9fff0  0xffff800007e9fff0
rsp            0xffff800007e9ff28  0xffff800007e9ff28
r8             0x0                 0
r9             0x0                 0
r10            0x84                132
r11            0xd                 13
r12            0x0                 0
r13            0x0                 0
r15            0x0                 0
rip            0xffffffff80001643  0xffffffff80001643 <gdt_init+17>
eflags         0x46                [ IOPL=0 ZF PF ]
cs             0x28                40
ss             0x30                48
ds             0x30                48
es             0x30                48
fs             0x30                48
gs             0x30                48
fs_base        0x0                 0
gs_base        0x0                 0
k_gs_base      0x0                 0
cr0            0x80010011          [ PG WP ET PE ]
cr3            0x7e8f000           [ PDBR=32399 PCID=0 ]
cr4            0x20                [ PAE ]
cr8            0x0                 0
efer           0xd00               [ NXE LMA LME ]
mxcsr          0x1f80              [ IM DM ZM OM UM PM ]

请注意,正如您所看到的,段寄存器已经设置为某个合理的值,这只是因为 Limine 加载了带有一些条目的默认 gdt。不过我想加载自己的。

© www.soinside.com 2019 - 2024. All rights reserved.