GDT表

11 篇文章 1 订阅
5 篇文章 0 订阅

gdt表在x86架构中用来存储内存的分段信息,通过段选择子进行访问,表的大小=0x10000=65536字节,每个表项占8字节,第一个表项为空,不使用,因此一共有8191个可用表项。表项结构如下

 

(图片来自https://blog.csdn.net/yeruby/article/details/39718119

其中段限长决定了这个段的大小,总共占用20bit,最小单位由G位决定,G=1,表示段的大小=段限长*4kb,G=0表示段的大小=段限长*1字节;

段基地址为该段的首地址,占用32bit;type说明了段的属性,网络资料很多,不再详述,占用4bit;

DPL为段的权限,占用2bit,只有访问程序的权限高于等于该段的权限才能使用这个段,权限分为4级,从高到低为0、1、2、3;

P=1说明该段可以使用,否则将当作该段不存在,如果使用段选择子强行访问会发生段异常,中断号为11;

AVL由软件设定,cpu不管这个位;

D/B有两种:当这个段被type设置为代码段时,D=1代表这是32位的程序,D=0代表这是16位的程序;如果是向下拓展的数据段,则称为B位,B=1代表最大可访问范围是4GB,B=0代表最大可访问范围是64k,如果表示的是堆栈段,则B=1,使用esp寄存器作为栈顶寄存器,否则使用sp寄存器

初始化gdt表是进入保护模式的关键步骤。首先将要设置的gdt表放到内存中的一个4字节对其的地址处(不建议放在0~0x800范围内,这里是实模式下中断向量表的位置,会影响进入保护模式之前的中断操作)。然后将6字节的表头加载到gdtr寄存器,例如:

lgdt ptr gdt_size

gdt_size:
dw 5*8-1        ;gdt表的长度,这里假设有5个表项(算上了gdt表最前面不使用的8字节)
dd gdt_table    ;gdt表的首地址

接着即使进入保护模式,令cr0寄存器的最低位等于1,就让处理器进入了保护模式,例:

mov eax,cr0
or eax,1
mov cr0,eax
jmp dword 0x08:start    ;清空流水线,并传串行化处理器。其中,0x08就是32位程序所在段的段选择子,结构见下面,start为偏移地址。

段选择子一共有16位,其中最低的2位1bit~0bit的是请求特权级,2bit叫做Ti,TI=0,说明这个在gdt中,TI=1,说明在LDT中。剩下的高13位就是选择第几个表项,比如0x08=0b00001000,请求特权级=0,TI=0,访问GDT表中的第一个表项(gdt表最开始的8字节表项是不用的,不然就叫第0个表项)。

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要了解一下全局描述符(Global Descriptor Table,GDT)是什么。GDT是在操作系统启动时由CPU初始化的一个数据结构,用于管理内存段的访问权限。操作系统可以通过修改GDT来控制内存的访问权限和保护。 在Linux系统中,GDT的初始化是在setup.s文件中完成的。下面是setup.s文件中初始化GDT的代码和注释: ``` /* * 初始化GDT */ init_gdt: lgdt gdtr /* 加载GDT寄存器 */ /* GDT表项0:空描述符 */ gdt_null: .word 0, 0 .byte 0, 0, 0, 0 /* GDT表项1:内核代码段描述符 */ gdt_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为0,系统段,粒度为4KB */ /* GDT表项2:内核数据段描述符 */ gdt_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为0,系统段,粒度为4KB */ /* GDT表项3:用户代码段描述符 */ gdt_user_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为3,用户段,粒度为4KB */ /* GDT表项4:用户数据段描述符 */ gdt_user_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为3,用户段,粒度为4KB */ /* GDT表项5:TSS描述符 */ gdt_tss: .word 0x67, 0 /* 段界限 */ .byte 0, 0, 0x89, 0x40 /* 基地址为tss_entry,DPL为0,系统段,粒度为1B */ .word 0 /* tss_entry的低16位 */ .byte 0 /* tss_entry的第24位 */ .byte 0x00 /* tss_entry的第32位 */ .word 0 /* tss_entry的高16位 */ /* GDT表项6:LDT描述符 */ gdt_ldt: .word 0, 0 /* 段界限 */ .byte 0, 0, 0x82, 0x40 /* 基地址为ldt,DPL为0,系统段,粒度为1B */ /* GDT表项7:TSS段 */ gdt_tss_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项8:LDT段 */ gdt_ldt_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项9:TSS段 */ gdt_tss2_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项10:LDT段 */ gdt_ldt2_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项11:TSS段 */ gdt_tss3_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT表项12:LDT段 */ gdt_ldt3_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ .fill 16 * 8 - (. - init_gdt) /* 填充到16字节 */ /* GDT */ gdt: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ /* GDT寄存器 */ gdtr: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ ``` 上述代码中,首先使用`lgdt`指令将GDT的长度和地址加载到GDT寄存器中。然后,定义了一些GDT表项,包括空描述符、内核代码段描述符、内核数据段描述符、用户代码段描述符、用户数据段描述符、TSS描述符和LDT描述符。其中,每个GDT表项包括段界限、基地址、段属性等信息。 最后,定义了一个GDT数组`gdt`,将GDT长度和地址存储在`gdtr`寄存器中,完成了GDT的初始化。 需要注意的是,上述代码只是一个简化的示例,实际的GDT表项可能更加复杂,具体的实现方式也可能因操作系统的不同而有所差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值