1

所以,我开始开发 x86_64 爱好内核,我发现这段代码可以加载 GDT(全局描述符表),但我不明白它的作用。

load_gdt:
  lgdt [rdi]
  mov ax, 0x10
  mov ss, ax
  mov ds, ax
  mov es, ax
  mov rax, qword .trampoline
  push qword 0x8
  push rax
  o64 retf

.trampoline:
    ret

我知道它从 rdi 寄存器(sysv abi 中函数调用的第一个参数的寄存器)加载我的 gdt 描述符,但我不知道为什么我需要将所有段寄存器设置为 0x10 以及什么黑魔法在做什么?

4

1 回答 1

1

一旦(新的)GDT 存在,将段 regs 设置为索引 GDT 的选择器就是您接下来要做的;这就是为什么你首先想要一个 GDT。或者如果您要更换旧的 GDT,例如来自 UEFI,则想要您自己的 GDT。(由于您已经处于 64 位模式,因此必须已经存在 GDT;mov rax, .trampoline与 RIP 相关的 LEA 相比,这是一个奇怪的选择,但涉及 REX 前缀,因此在 32 位模式下会解码错误。)

最后几个是用远跳设置 CS,通过推送一个新的 CS:RIP 并做一个 far-ret 将其弹出到 CS:RIP 中来完成。它需要是 64 位操作数大小,以确保您从堆栈中弹出 64 位 RIP,而不是 32 位 EIP。

(你当然mov不能使用 CS;那将是一个跳转,因为它会改变代码获取的来源。x86 只允许通过调用 / jmp 或 retf 等跳转指令编写 E/RIP 或 CS:E/RIP。 )

另见https://wiki.osdev.org/Global_Descriptor_Table

于 2021-12-17T16:29:39.787 回答