【问题标题】:How is the global descriptor table copied in MINIX assembly code (x86)MINIX汇编代码(x86)中如何复制全局描述符表
【发布时间】:2018-07-16 01:43:22
【问题描述】:

MINIX 3 中的这段代码将引导监视器的(引导)GDT 复制到内核空间并切换它。但是我很难理解代码。在代码中,_gdt 是在 C 中声明的描述符表数组的地址 (gdt[GDT_SIZE])。 gdt的结构如下:

struct segdesc_s {      /* segment descriptor for protected mode */
  u16_t limit_low;
  u16_t base_low;
  u8_t base_middle;
  u8_t access;          /* |P|DL|1|X|E|R|A| */
  u8_t granularity;     /* |G|X|0|A|LIMT| */
  u8_t base_high;
};

结构的大小为 8 个字节。宏 GDT_SELECTOR 的值为 8

! Copy the monitor global descriptor table to the address space of kernel and
! switch over to it.  Prot_init() can then update it with immediate effect.

     sgdt   (_gdt+GDT_SELECTOR)     ! get the monitor gdtr
     mov    esi, (_gdt+GDT_SELECTOR+2)  ! absolute address of GDT
     mov    ebx, _gdt           ! address of kernel GDT
     mov    ecx, 8*8            ! copying eight descriptors
copygdt:
eseg movb   al, (esi)
     movb   (ebx), al
     inc    esi
     inc    ebx
     loop copygdt

最令人困惑的一行是movb (ebx), al。请帮忙。

【问题讨论】:

    标签: assembly x86 operating-system minix


    【解决方案1】:

    这是一种奇怪的 asm 语法。它使用() 来处理像 AT&T 语法这样的内存操作数,但只有当它像 Intel 语法一样位于左侧时才有意义。 (它还使用 AT&T 风格的助记符后缀来表示操作数大小,例如 movb 表示字节 mov。)

    我认为它基本上是 NASM 语法,但使用 () 而不是 [],因为评论说 mov ebx, _gdt 是地址的 mov-immediate。在 GAS .intel_syntax noprefix 中,这将是 MASM 语法中的负载。

    Minix 的编译器有它自己的 asm 风格,它是 documented here(感谢 @MichaelPetch)。


    所以这是一个字节一次的复制循环,从es:esids:edi,对于ecx=8*8 字节。 这正是 cmets 所说的,所以这样可以很容易地找出我以前从未见过的语法。

    movb (ebx), al 将 AL 存储到内存中,位于 EBX 中的地址。即 NASM mov [ebx], al 或 AT&T mov %al, (%ebx)


    商店正在使用 EBX 的默认段选择器,即 DS。您通常不需要提及 32 位模式下的段,但请注意负载上的 eseg 前缀。您没有显示,cmets 也没有提及 ES 设置为什么,以及它与 DS 有何不同/有何不同。

    似乎代码针对代码大小而不是速度进行了优化(这没关系,因为它只在启动时运行一次)。例如它使用慢速loop 指令,一次复制一个字节,因此它可以inc 指针(1 个字节)而不是add esi, 4(3 个字节)。不过,我怀疑使用索引寻址模式,你可以让它变得很小,但一次复制 4 个字节。 (字节数固定为8*8,所以总是4的倍数。)

    循环非常接近rep movsb (or rep movsd)所做的,即将ecx元素从DS:(E)SI复制到ES:(E)DI。 (ds 可以用段前缀覆盖,因此您可以例如从fs:esi 复制到es:edi)。但在 Minix 代码中,负载来自 ES:something,movs 始终使用es 作为目标段。

    fseg rep movsd 会比循环更紧凑(更快),但大概在正确设置段寄存器方面存在一些障碍。使用 EDI 和 ESI 代替 ESI 和 EBX 应该不是障碍。

    【讨论】:

    • 语法正确,因为它是 Minix CC/程序集语法(基于 Intel)。括号与方括号的含义相同。
    • @SuvratApte sgdt 返回 6 个字节,并将这 6 个字节存储在 _gdt+GDT_SELECTOR 中。前两个字节是 GDT 的长度,其余 4 个字节是 GDT 的地址。 (_gdt+GDT_SELECTOR+2) 然后是您需要复制的 GDT 的基地址。 mov esi,, (_gdt+GDT_SELECTOR+2)` 中的括号表示获取该位置的 4 个字节并将其存储到 ESI。
    • @SuvratApte SGDT 不返回 GDT 本身。它返回一个 6 字节结构,其中包含一个 16 位字,表示 GDT 的长度(减 1),后跟一个 32 位地址,它是 GDT 的基址。这个 6 字节结构存储在 GDTR(GDT 寄存器)中。您可以在此处查看 GDTR 的图片:wiki.osdev.org/Global_Descriptor_Table
    • @PeterCordes:需要避免fseg rep movs 有两个原因:1) 早期的 80386 芯片在指令有两个前缀时存在错误,因此直到 1993 年程序员经常避免这种结构,并且这段代码写于 1992 年; 2)这段代码是为8088 PC编写的,它们没有FS段。
    • @PeterCordes:首先,我的错误,这段代码最初是为 80286 PC 编写的。它显然使用 32 位寄存器的事实是因为 Minix 3 放弃了 16 位保护模式;但是如果您查看 Minix 2,您会发现 16 位和 32 位都使用相同的代码。保持这些关键代码(调试它很困难,而且当我们没有虚拟机时更难)保持简单很重要,而且当它工作时,没有令人信服的理由去改变它;正如您之前提到的,表演肯定不是冒险休息的可能理由!
    最近更新 更多