在由实模式进入保护模式的时候,GDT是一个绕不开的知识点,而网上讲的都太理论化,我结合了好多篇不同作者写的文章才算把它弄懂,下面我直接用直观的方式来展现这个东西。
假设我们的16位程序加载于: 0192h:0100h jmp 16位程序段 gdt_table 16位程序段 lgdt jmp 32位程序段 我们的32位程序开始于: 0192h:0200h 32位程序内容 结束于: 0192h:0300h 那么我们32位程序的线性地址(真实物理地址)为: 01920h + 0200h = 1B20h ; 该地址即为32位程序的段基地址 Base 0:32 = 0x00001b20 段界限为: 0300h - 0200h = 100h ; 该差值即为32位程序的段界限 Limit 0:19 = 0x00100 该值最大长度为20位,多了没处放哈
desc_gdt_start: ; 每一条段描述符都是8字节,64位 desc_def: dd 0 , 0 ; 第一条必须是空的描述符,后面的顺序任意 ; 段基址占4个字节,32位,GDT里头会分成几部分存 ; 假设我们的 Code32 所在的段地址为: desc_code32: db 0x00, ; Limit 0:7 0x001(00) db 0x01, ; Limit 8:15 0x0(01)00 db 0x20, ; Base 0:7 0x00001b(20) db 0x1b, ; Base 8:15 0x0000(1b)20 db 0x00, ; Base 16:23 0x00(00)1b20 db 0x98, ; Access Byte db 0x40 ; Flag=4 Limit 16:19 0x(0)0100 db 0x00 ; Base 24:31 段地址24-31位 0x(00)001b20 ; 显存固定的,不用计算 段地址: 0x000b8000, 段界限: 0x0fffff, 属性:0x92 desc_video: db 0xff, ; Limit 0:7 0x0ff(ff) db 0xff, ; Limit 8:15 0x0(ff)ff db 0x00, ; Base 0:7 0x000b80(00) db 0x80, ; Base 8:15 0x000b(80)00 db 0x0b, ; Base 16:23 0x00(0b)8000 db 0x92, ; Access Byte 0x92 db 0xff ; Flag + Limit 16:19 0x(f)ffff db 0x00 ; Base 24:31 段地址24-31位 0x(00)0b8000 desc_gdt_end: gdt_ptr equ desc_gdt_end - desc_gdt_start dd 0 selector_code32 equ desc_code32 - desc_gdt_start selector_video equ desc_video - desc_gdt_start
对照下代码来理解这个属性图: db 0x98, ; Access Byte db 0x40 ; Flag=4 Limit 16:19 0x(0)0100 AccessByte: 0x98 换算成二进制是 10011000 p = 1b, 1 表示内存中存在, 0 表示不存在 DPL = 00b, 表示描述符特权级【0,1,2,3】,值越小权限越高 S = 1b, 1 表示数据段, 0 表示系统段 TYPE = 1000b 该值为十进制8,查表得知其意为:只执行
Flag=4: 0x4 换算成二进制是 0100(另外4位被Limit用了) G = 0b, 0 表示段界限粒度为字节, 1 表示段界限为4KB D/B = 1b, 在可执行代码段中,该值为D位,D=1表示32位地址及32、8位操作数, D=0表示16位地址及16、8位操作数 向下扩展数据段中,该值为B位,B=1表示段的上部界限为4GB, B=0表示段的上部界限为64KB 在描述堆栈的时候,该值为B位,B=1表示使用32位段寄存器 esp,D=0表示使用16位段寄存器 sp 0, 未使用,0,1都可以 VAL = 0b 该值为保留位,暂时没啥用,以后可被系统软件使用