Global Descriptor Table
全局描述符表(Global Descriptor Table - GDT)是特定于IA-32和x86-64体系结构的二进制数据结构。 它包含告诉CPU有关内存分段的条目(Entry)。 还存在一个类似的 中断描述符表(Interrupt Descriptor Table),其中包含 任务 和 中断 描述符。
建议阅读GDT教程。
GDTR寄存器
GDT由GDTR寄存器中的值指向。 这是使用LGDT汇编指令加载的,该指令的参数是指向GDT描述符结构的指针:
79 (64-bit mode) 48 (32-bit mode) 16 |
15 0 |
---|---|
Offset 63 (64-bit mode) 31 (32-bit mode) 0 |
Size 15 0 |
- Size: 表的大小(以字节为单位)减去1。 之所以发生这种减法,是因为 size 的最大值是65535的,而GDT的长度最多可达65536个字节 (8192个条目)。 此外,gdt不可以具有0字节的大小。
- Offset: GDT的线性地址(不是物理地址,分页适用)。
请注意,LGDT加载的数据量在32位和64位模式中不同,offset在32位模式中为4字节长,在64位模式中为8字节长。
有关更多信息,请参阅《英特尔软件开发人员手册》 (第3-A卷) 的 Section 2.4.1: Global Descriptor Table Register (GDTR) 和 Figure 2-6: Memory Management Registers。
表说明
GDT中的条目长度为8字节,形成如下表格:
地址 | 内容 |
---|---|
GDTR Offset + 0 | Null |
GDTR Offset + 8 | Entry 1 |
GDTR Offset + 16 | Entry 2 |
GDTR Offset + 24 | Entry 3 |
... | ... |
GDT (条目0) 中的第一个条目应始终为null,只使用后续条目。
表中的条目由段选择器(Segment Selectors)访问,这些条目通过汇编指令或硬件功能(如中断)加载到段寄存器中。
段描述符(Segment Descriptor)
上面表中的每个条目(Entry)都有一个复杂的结构:
63 56 | 55 52 | 51 48 | 47 40 | 39 32 |
---|---|---|---|---|
Base 31 24 |
Flags 3 0 |
Limit 19 16 |
Access Byte 7 0 |
Base 23 16 |
31 16 | 15 0 | |||
Base 15 0 |
Limit 15 0 |
- Base: 一个32位的值,包含段开始的线性地址。
- Limit: 20位值告诉最大可寻址单元,以1字节为单位,或以4KiB页为单位。 因此,如果选择页面粒度并将 Limit 值设置为0xFFFFF,则该段将在32位模式下跨越整个4 GiB地址空间。
在64位模式下,忽略Base和Limit值,每个描述符覆盖整个线性地址空间,不管它们设置为什么。
有关详细信息,请参阅《英特尔软件开发人员手册》,第3-A卷的Section 3.4.5: Segment Descriptors和Figure 3-8: Segment Descriptor。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
P | DPL | S | E | DC | RW | A |
- P: Present位。 启用条目为引用向有效段。 必须为任何有效段设(1)。
- DPL: 描述符特权级别字段。 包含段的 CPU特权级别 0 =最高权限(内核),3 = 最低权限(用户应用程序)。
- S: 描述符类型位。 如果清 (0),则描述符定义一个系统段 (例如 任务状态段-Task State Segment)。 如果设(1),则定义代码或数据段。
- E: Executable(可执行)位。 如果清(0),则描述符定义数据段。 如果设(1),它定义了一个可以用于执行的代码段。
- DC: Direction位/Conforming位。
- 对于数据(Data)选择器: Direction 位。 如果清(0),则该段将向上增长。 如果设(1)段向下增长,即Offset必须大于Limit。
- 对于代码(Code)选择器:Conforming 位。
- 如果清(0)该段中的代码只能从Privl中设置的Ring执行。
- 如果设(1),此段可以从相等或较低的特权级别执行代码。 例如,Ring3中的代码可以跳转到Ring2段中的conforming代码。 Privl字段表示允许执行该段的最高权限级别。 例如,Ring0中的代码不能far-jump到 Privl 为2的conforming代码段,而ring2和ring中的代码可以。 请注意,跳转后特权级别保持不变,即从Ring3到Privl为2的段的far-jump仍保留在Ring3中。
- RW: 可读位/可写位。
- 对于代码段:可读位。 如果清(0),则不允许对此段进行读取访问。 如果设(1),则允许读取访问。 永远不允许对代码段进行写访问。
- 对于数据段:可写位。 如果清 (0),则不允许对此段进行写访问。 如果设(1),则允许写访问。 数据段始终允许读取访问。
- A: Accessed(已访问)位。 最好清(0),CPU将在访问段时设它为1。
3 | 2 | 1 | 0 |
---|---|---|---|
G | DB | L | Reserved |
- G: 粒度(Granularity )标志,指示Limit值的缩放大小。 如果清 (0),则 Limit 以1字节块为单位 (字节粒度)。 如果设(1),Limit单位是4KiB块(页面粒度)。
- DB: Size标志。 如果清(0),则描述符定义一个16位保护模式的段。 如果设 (1),它定义了一个32位受保护模式的段。 GDT可以同时具有16位和32位选择器。
- L: 长模式代码标志。 如果设(1),则描述符定义64位代码段。 当它设1时,Sz 应始终清0。 对于任何其它类型的段(其它代码类型或任何数据段),它应该要清(0)。
系统段描述符(System Segment Descriptor)
对于系统段,例如定义 任务状态段(Task State Segment) 或 本地描述符表(Local Descriptor Table) 的系统段, Access Byte的格式略有不同,以便定义不同类型的系统段,而不仅仅是代码段和数据段。
有关详细信息,请参阅《英特尔软件开发人员手册》,Section 3.5: System Descriptor Types和Section 3.5: System Descriptor Types。
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
P | DPL | S | Type |
- Type: 系统段的类型。
32位保护模式下可用的类型:
- 0x1: 16-bit TSS (Available)
- 0x2: LDT
- 0x3: 16-bit TSS (Busy)
- 0x9: 32-bit TSS (Available)
- 0xB: 32-bit TSS (Available)
长模式下可用的类型:
- 0x2: LDT
- 0x9: 64-bit TSS (Available)
- 0xB: 64-bit TSS (Available)
长模式系统段描述符(Long Mode System Segment Descriptor)
对于长模式(Long Mode)中的Task State Segment或Local Descriptor Table。 段描述符(Segment Descriptor)的格式不同,以确保 Base 值可以包含64位 线性地址。 它需要占用了两个一般条目表中的空间,且采用一种小端格式,因此该条目的下半部分在表中的上半部分之前。
有关详细信息,请参阅《英特尔软件开发人员手册》,第3-A卷的Section 7.2.3: TSS Descriptor in 64-bit Mode和Figure 7-4: Format of TSS and LDT Descriptors in 64-bit Mode。
127 96 | ||||
---|---|---|---|---|
Reserved | ||||
95 64 | ||||
Base 63 32 | ||||
63 56 | 55 52 | 51 48 | 47 40 | 39 32 |
Base 31 24 |
Flags 3 0 |
Limit 19 16 |
Access Byte 7 0 |
Base 23 16 |
31 16 | 15 0 | |||
Base 15 0 |
Limit 15 0 |
另见
文章
- GDT教程
- 到达Ring 3
- 分段
- http://www.osdever.net/tutorials/view/the-world-of-protected-mode - how to set up GDT in assembler