My first blog: GDT与基本描述符的结构
参考文章:Intel64 and IA32 Architectures Software Developer's Manual Volume 2 ~ 3
GDT(Global Descriptor Table,全局描述符表)是x86和amd64架构最最基本的数据结构,它存储在内存中,以GDTR记录基地址,是让电脑从实模式跳转到保护模式的台阶。在汇编代码中,我们可以用 lgdt [gdt_base] 来加载GDT到内存中。
GDT的结构是这样的:
GDTR.base-> _________________________ \
|________``````````_______| |
|_________Items___________| | <- GDTR.limit
|________``````````_______| /
然后GDTR的结构是这样的:
79 16 15 0
|________________Base_________________|____Limit_____|
其实,它并没有这么高深,而只是一个表状结构而已。而里边的表项(描述符)则因为历史原因被分割得乱七八糟。
在实模式里,描述符(没此一说)就是个long-jmp pointer,如下所示。
31 16 15 0
|_________Base__________|________Limit________|
而在保护模式里,它分成了两类:段描述符和系统描述符
段描述符的格式如下:
63 56 55 54 53 52 51 48 47 46 45 44 43 40 39 16 15 0
| Base(31:24) | G|DB| L|AVL|Limit(19:16)| P| DPL | S| Type |_______________Base(23:0)___________________|________Limit(15:0)___|
Granularity-----^ ^ ^ ^ ^ ^ ^ ^
Default op size----' | | | | | |
Long mode-------------' | | | | |
Available for developers-' | | | |
Present-----------------------------------' | | |
Descriptor Privilege Level-------------------' | |
Is system descriptor-------------------------------' |
Descriptor type: details under------------------------'
1 struct SEG_DESCRIPTOR 2 { 3 uint16_t Limit_Low; 4 uint16_t Base_Low; 5 uint8_t Base_Mid; 6 uint8_t Attr_Low; 7 uint8_t Attr_High__Lim_High; 8 uint8_t Base_High; 9 } __attribute__((packed));
Type字段和S字段指示了描述符的类型:S=0的情况暂不讨论。
3 2 1 0
-------------------
0: E W A : Data Segment
-------------------
1: C R A : Code Segment
当描述符是数据段时,Type.E表示Expand的方向,默认(0)为Expand-up;Type.W表示Writable,默认(0)表示Read-only;而Type.A表示Accessed,当描述符所表示的段被访问时,处理器会自动置这个字段为1。
当描述符是代码段时,Type.C表示Conforming,这个字段以后讨论;Type.R表示Readable,默认(0)表示不可读(只可执行);Type.A同上。
1 #define DATA_Org 0x10 2 #define DATA_W 0x12 3 #define CODE_Org 0x18 4 #define CODE_R 0x1A 5 #define CODE_C 0x1C 6 #define CODE_CR 0x1E
DPL字段是访问描述符需要的权限等级。这就是保护模式的意义所在之一。
P字段表示描述符是否存在。
AVL字段是留给开发者用的,不过我感觉实质上没什么用。
L字段和DB字段指示代码段的bits。如下:
L DB
0 0 --> 16位,即实模式
0 1 --> 32位,即保护模式
1 0 --> 不合法(加载时跳#GP)
1 1 --> 64位,即长模式
G字段表示段的粒度,0表示1字节,1表示4KB。一般把G置1,limit设成0xFFFFF就是所谓的4GB Flat Segmentation,即平坦分段模式。
上述字段在保护模式都可用,然而到了长模式里,仅剩Type、S、DPL、P、L可用,其他一律忽略(除limit、DB、G全部位置1外,其他一律为0),统统变成Flat Mode,且limit再也不会检查了。因为在长模式里,分段内存管理变得次要,转而抬升为头等的是分页内存管理。这并不代表GDT的作用减弱。因为除了段描述符外,我们还有一大批系统描述符没讲。
本文到此结束。如有错误望指正。
附:汇编静态创建段描述符
1 ; 1:Base 2:Limit 3:Attribute 2 ; 所有参数按段描述符基地址设置。 3 %macro SEGDESC 3 4 dw %2 & 0xFFFF 5 dw %1 & 0xFFFF 6 db (%1 >> 16) & 0xFF 7 db %3 & 0xFF 8 db (%3 >> 8) & 0xF0 | (%2 >> 16) & 0xF 9 db (%1 >> 24) & 0xFF 10 %endmacro
动态创建段描述符
1 ; rdi: 描述符基地址 esi:Base r8d:Limit r9w: Attr 2 create_segdesc: 3 mov [rdi], r8w 4 mov [rdi + 16], si 5 shr r8d, 16 6 shr esi, 16 7 mov [rdi + 32], si 8 and r9w, 0F0FFh 9 shl r8w, 8 10 or r9w, r8w 11 mov [rdi + 40], r9w 12 and si, 0FF00h 13 and word [rdi + 48], 0FFh 14 or [rdi + 48], si
15 ret