GDT(全局描述符表)和LDT(局部描述符表)————[GDT、LDT、GDTR、LDTR解析]

Posted by 许大仙 on July 24, 2018

每个程序都有自己的LDT,但是同一台计算机上的所有程序共享一个GDT。

  • LDT描述局部于每个程序的段,包括其代码、数据、堆栈等。
  • GDT描述系统段,包括操作系统本身。

一、引入

保护模式[Protected Mode]下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成

  • 段描述符寄存器: 存储段描述符

  • 选择器:存储段描述符的索引

段寄存器构成形式

PS:原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)80位的段寄存器。由选择器CS对应表示的段仍为代码段,选择器SS对应表示的段仍为堆栈段。

二、GDT详解

先说明一下概念

(1)全局描述符表GDT(Global Descriptor Table)在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT)GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了GDTR中存放的是GDT在内存中的基地址和其表长界限。

基地址指定GDT表中字节0在线性地址空间中的地址,表长度指明GDT表的字节长度值。指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容

GDTR寄存器

在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。在保护模式初始化过程中必须给GDTR加载一个新值。

(2)段选择子(Selector,即前述的选择器)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的[也就是对GDTR进行分割]。段选择子是一个16位的寄存器(同实模式下的段寄存器相同)

Selector

段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。

  • index(描述符索引)部分:表示所需要的段的描述符在描述符表的位置,index+GDTR中存储的描述符表基址(32位)就可以找到所需相应段的描述符[即段描述符寄存器,其中有段基址]。然后用相应段描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址[得到具体所需变量的地址,用于重定位],段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。[注:GDTR是全局描述符表寄存器,不是段描述符寄存器]
  • 请求特权级(RPL):代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。[2位二进制,标识4个特权级]

关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一特权级或级别较低特权级的段

示例

例如给出逻辑地址:21H:12345678H转换为线性地址 [0021H为16位的段选择子,0021H=0000 0000 0010 0001B]

  1. 选择子SEL=21h=0000000000100 0 01B 他代表的意思是:选择子的index=4即100B,选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;最后的01B代表特权级RPL=1

  2. OFFSET=12345678H,若此时GDT第四个描述符中描述的段基址(Base)为11111111H[通过GDTR中的32位基址+index得到GDT第四个描述符,由段描述符寄存器给出信息,其中有段基址11111111H],则线性地址=11111111H+12345678H=23456789H

三、LDT详解

(1)局部描述符表LDT(Local Descriptor Table):局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。

GDT包含系统使用的代码段、数据段、堆栈段和特殊数据段描述符,以及所有任务局部描述符表LDT的描述符。

LDT与GDT的关系

LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同,LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子[LDT表也是一个段,在GDT表中也有对应的选择子定位它,LDTR就装载了定位这个LDT的选择子],LDTR装载的就是这样一个选择子。LDTR可以在程序中随时改变[通过使用LLDT指令]。如上图,如果装载的是Selector 2则LDTR指向的是表LDT2。

示例

举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。

  1. 首先需要装载LDTR使它指向LDT2 使用指令LLDT将Select2装载到LDTR,此时LDTR指向的是LDT2.

  2. 在LDT2中,按照例子中的要求选择LDT2中的第三个描述符。则通过逻辑地址(SEL:OFFSET)访问时,可推测出SEL的index=3代表选择第三个描述符[即二进制的11B];TI=1代表选择子是在LDT选择[1B],,所以在LDT2中选择时,此时的SEL值为1CH(二进制为11 1 00B)。OFFSET=12345678h。得到逻辑地址为1C:12345678h

  3. 由SEL选择出段描述符,由段描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789H

  4. 此时若再想访问LDT1中的第三个描述符,只要使用LLDT指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)

由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。[在LDT表中找到的该进程的段基址,就标识了该进程的各个段的位置。这个段基址放在段描述符寄存器里面]

当进行任务切换时,处理器会把新任务LDT的段选择符和段描述符自动地加载进LDTR中。在机器加电或处理器复位后,段选择符和基地址被默认地设置为0,而段长度被设置成0xFFFF。

四、实例

①访问GDT

GDT全局图

当TI=0时表示段描述符在GDT中,如上图所示:

  1. 先从GDTR寄存器中获得GDT基址。

  2. 然后再GDT中以段选择器[段选择子]高13位位置索引值得到段描述符。

  3. 段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的线性地址[偏移地址就是CPU要访问变量的虚存地址]。

②访问LDT

段描述符在LDT中

当TI=1时表示段描述符在LDT中,如上图所示:

  1. 还是先从GDTR寄存器中获得GDT基址。

  2. 从LDTR寄存器[就是一个选择子,用于定位某进程的LDT表]中获取LDT所在段的位置索引(LDTR高13位)。

  3. 以这个位置索引在GDT中得到LDT段描述符从而得到该进程LDT段基址[得到内存中对应的LDT表位置]。

  4. 在LDT表中,根据CPU访问进程中某变量的逻辑地址[SEL:OFFSET],用SEL段选择器高13位位置索引值从已选择出的LDT段中得到段描述符。

  5. 段描述符符包含段的基址、限长、优先级等各种属性,这就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到变量最后的线性地址。

注意:访问GDT表中的系统段描述符时,根据系统变量逻辑地址中的SEL就能确定GDT表中的系统描述符位置。但是对于进程内的变量,在进程切换时,处理器会把新任务LDT的段选择符和段描述符自动地加载进LDTR中[LLDT指令->LDTR]。然后根据LDTR选择出GDT表中的LDT表,再由进程变量逻辑地址中的SEL,确定LDT表中的段描述符

五、扩展

除了GDTR、LDTR外还有IDTR和TR

(1)中断描述符表寄存器IDTR

与GDTR的作用类似,IDTR寄存器用于存放中断描述符表IDT的32位线性基地址和16位表长度值。指令LIDT和SIDT分别用于加载和保存IDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。

(2)任务寄存器TR

TR用于寻址一个特殊的任务状态段(Task State Segment,TSS)。TSS中包含着当前执行任务的重要信息。

TR寄存器用于存放当前任务TSS段的16位段选择符、32位基地址、16位段长度和描述符属性值。它引用GDT表中的一个TSS类型的描述符。指令LTR和STR分别用于加载和保存TR寄存器的段选择符部分。当使用LTR指令把选择符加载进任务寄存器时,TSS描述符中的段基地址、段限长度以及描述符属性会被自动加载到任务寄存器中。当执行任务切换时,处理器会把新任务的TSS的段选择符和段描述符自动加载进任务寄存器TR中。