【问题标题】:Far jump in gdt in bootloader引导加载程序中的 gdt 远跳
【发布时间】:2014-07-21 14:58:06
【问题描述】:
flush_gdt:
    lgdt [gdtr]
    jmp 0x08:complete_flush

complete_flush:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    ret

我无法理解这段代码的作用。 flush_gdt 是一个标签 OK ,然后 lgdt [gdtr]48-bit 指针加载到 gdtr 寄存器中,然后从 jmp 0x08:complet_flush 加载。

jmp 指令有什么作用?那么为什么我们将 0x10 移动到 ax ,然后再移动到其他寄存器

【问题讨论】:

  • 我认为您需要更具体地说明您对此不了解的地方?
  • 我已经更新了问题请看一下

标签: assembly x86 bootloader gdt


【解决方案1】:

x86 支持两种虚拟内存方案(read about it here):

  • 分段,必须,使用分段表 GDT 管理。
  • 分页,可选,使用页表 PDT 管理。

大多数操作系统都希望使用分页而不想要分段,但它必须而且不能只是禁用。

所以诀窍是禁用它的效果,因为它不存在。这通常可以通过创建 4 个大型 重叠 段描述符(在空段旁边)来完成:

  • 段索引 0:空段描述符
  • 段索引 1:特权(内核)模式的代码段描述符
  • 段索引 2:特权(内核)模式的数据段描述符
  • 段索引 3:非特权(用户)模式的代码段描述符
  • 段索引 4:非特权(用户)模式的数据段描述符

所有这些段都从0x000000000xffffffff 开始,因此您最终会得到重叠的大段,即特权代码和数据,以及非特权代码和数据。这应该会打开虚拟内存并禁用分段效果。

处理器使用段选择器(段寄存器csdsss ...)来找出正确的段(再一次,分段是必须的)。

每个段选择器都是 16 位大小并具有以下布局 (source):

  • 前两位表示权限级别,x86支持4个级别,但实际使用的只有两个(00最高,11最低)。

  • 第三位表示应该使用该表,主要是0,GDT。

  • 其余 13 位表示段索引。

如果您解释了在cs 中加载的0x08,它将是二进制的:

0000000000001     0         00
index 1 (code)   GDT    privileged

以及在dsss、...中加载的0x10

0000000000010     0         00
index 2 (data)   GDT    privileged

如果您阅读任何用户模式程序的段选择器,您应该会看到 cs 的值是 27 (0x1b),这意味着:

0000000000011     0         11
index 3 (code)   GDT   non-privileged

数据选择器dsss、...应该存储35(0x23):

0000000000100     0         11
index 4 (data)   GDT   non-privileged

数据段选择器(寄存器),可以使用简单的mov 指令轻松修改,但cs 不能与mov 一起使用,因此您使用jmp 0x08:OFFSET 将段配置加载到代码段选择器。

【讨论】:

  • 当我们在做 jmp 0x08:OFFSET 时,所以我们用0x08 加载CSOFFSET 加载IP,对吧?
  • 好了,这里是我对代码的理解,告诉我是对是错。由于这应该处于保护模式,jmp 指令设置代码段的描述符,然后跳转到complete_flush,它设置其他描述符并返回,对吧?
  • 非常感谢@BSH,这是一个很好的答案! :)
  • @AbKDs,值得注意的是,段选择器隐式包含在每个代码/数据访问中。这是可行的,因为选择器也可以显式包含在内。加载后,每个代码访问有效cs:addr,每个内存访问ds:addr
  • 毫无疑问,这是对现有一些复制粘贴最多的代码的最简洁的解释之一。这值得存档。
猜你喜欢
  • 2020-07-02
  • 1970-01-01
  • 2013-06-13
  • 2017-11-03
  • 2014-10-24
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
相关资源
最近更新 更多