前面我们使用的都是全局段描述符表,现在我们来分析局部段描述符表的使用。
什么是LDT(Local Descriptor Table)?
局部段描述符表:
本质是一个段描述符表,用于定义段描述符
与GDT类似,可以看做“段描述符的数组”
通过定义选择子访问局部段描述符表中的元素
局部段描述符的选择子和全局描述符的选择子在结构上是完全一样的,3-15位为描述符索引值,LDT选择子的第二位恒为1,1-0位为RPL。
局部段描述符表就是一段内存,里面的每一项是一个局部段描述符(第0项也是有意义的),用于描述一段内存。
CPU中有一个专用寄存器专门指向局部段描述符表。(先定义一个常量 DA_LDT equ 0x82),在全局段描述符表中定义局部段描述符表的描述项时,需要用到属性,这个属性就是DA_LDT。
局部段描述符表的注意事项:
局部段描述符表需要在全局段描述符表中注册(增加描述项)
通过对应的选择子加载局部段描述符(lldt)
局部段描述符表从第0项开始使用(different from GDT)
LDT的意义:
代码层面的意义:
分级管理功能相同意义不同的段(如:多个代码段),全局段描述符表也是有界限的,如果分段过多,则全局段描述符表有可能不够用。而局部段描述符表不限制描述符的个数。引入这种分级管理描述符的机制,可以定义无数个段。
系统层面的意义:
实现多任务的基础要素(每个任务对应一系列不同的段)
LDT的定义与使用:
1、定义独立功能相关的段(代码段、数据段、栈段)
2、将目标段描述符组成局部段描述符表(LDT)
3、为各个段描述符定义选择子(SA_TIL)
4、在GDT中定义LDT的段描述符,并定义选择子
下面给出示例程序,演示局部段描述符的使用。
inc.asm更新如下:
1 ; Segment Attribute 2 DA_32 equ 0x4000 3 DA_DR equ 0x90 4 DA_DRW equ 0x92 5 DA_DRWA equ 0x93 6 DA_C equ 0x98 7 DA_CR equ 0x9A 8 DA_CCO equ 0x9C 9 DA_CCOR equ 0x9E 10 11 ; Special Attribute 12 DA_LDT equ 0x82 13 14 ; Selector Attribute 15 SA_RPL0 equ 0 16 SA_RPL1 equ 1 17 SA_RPL2 equ 2 18 SA_RPL3 equ 3 19 20 SA_TIG equ 0 21 SA_TIL equ 4 22 23 ; 描述符 24 ; usage: Descriptor Base, Limit, Attr 25 ; Base: dd 26 ; Limit: dd (low 20 bits available) 27 ; Attr: dw (lower 4 bits of higher byte are always 0) 28 %macro Descriptor 3 ; 段基址, 段界限, 段属性 29 dw %2 & 0xFFFF ; 段界限1 30 dw %1 & 0xFFFF ; 段基址1 31 db (%1 >> 16) & 0xFF ; 段基址2 32 dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2 33 db (%1 >> 24) & 0xFF ; 段基址3 34 %endmacro ; 共 8 字节
我们增加了第12行的宏定义。
loader.asm的程序如下:
1 %include "inc.asm" 2 3 org 0x9000 4 5 jmp ENTRY_SEGMENT 6 7 [section .gdt] 8 ; GDT definition 9 ; 段基址, 段界限, 段属性 10 GDT_ENTRY : Descriptor 0, 0, 0 11 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 12 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 13 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32 14 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 15 CODE16_DESC : Descriptor 0, 0xFFFF, DA_C 16 UPDATE_DESC : Descriptor 0, 0xFFFF, DA_DRW 17 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT 18 ; GDT end 19 20 GdtLen equ $ - GDT_ENTRY 21 22 GdtPtr: 23 dw GdtLen - 1 24 dd 0 25 26 27 ; GDT Selector 28 29 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 30 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 31 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 32 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 33 Code16Selector equ (0x0005 << 3) + SA_TIG + SA_RPL0 34 UpdateSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 35 TaskALdtSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0 36 ; end of [section .gdt] 37 38 TopOfStack16 equ 0x7c00 39 40 [section .dat] 41 [bits 32] 42 DATA32_SEGMENT: 43 DTOS db "D.T.OS!", 0 44 DTOS_OFFSET equ DTOS - $$ 45 HELLO_WORLD db "Hello World!", 0 46 HELLO_WORLD_OFFSET equ HELLO_WORLD - $$ 47 48 Data32SegLen equ $ - DATA32_SEGMENT 49 50 [section .s16] 51 [bits 16] 52 ENTRY_SEGMENT: 53 mov ax, cs 54 mov ds, ax 55 mov es, ax 56 mov ss, ax 57 mov sp, TopOfStack16 58 59 mov [BACK_TO_REAL_MODE + 3], ax 60 61 ; initialize GDT for 32 bits code segment 62 mov esi, CODE32_SEGMENT 63 mov edi, CODE32_DESC 64 65 call InitDescItem 66 67 mov esi, DATA32_SEGMENT 68 mov edi, DATA32_DESC 69 70 call InitDescItem 71 72 mov esi, DATA32_SEGMENT 73 mov edi, STACK32_DESC 74 75 call InitDescItem 76 77 mov esi, CODE16_SEGMENT 78 mov edi, CODE16_DESC 79 80 call InitDescItem 81 82 mov esi, TASK_A_LDT_ENTRY 83 mov edi, TASK_A_LDT_DESC 84 85 call InitDescItem 86 87 mov esi, TASK_A_CODE32_SEGMENT 88 mov edi, TASK_A_CODE32_DESC 89 90 call InitDescItem 91 92 mov esi, TASK_A_DATA32_SEGMENT 93 mov edi, TASK_A_DATA32_DESC 94 95 call InitDescItem 96 97 mov esi, TASK_A_STACK32_SEGMENT 98 mov edi, TASK_A_STACK32_DESC 99 100 call InitDescItem 101 102 ; initialize GDT pointer struct 103 mov eax, 0 104 mov ax, ds 105 shl eax, 4 106 add eax, GDT_ENTRY 107 mov dword [GdtPtr + 2], eax 108 109 ; 1. load GDT 110 lgdt [GdtPtr] 111 112 ; 2. close interrupt 113 cli 114 115 ; 3. open A20 116 in al, 0x92 117 or al, 00000010b 118 out 0x92, al 119 120 ; 4. enter protect mode 121 mov eax, cr0 122 or eax, 0x01 123 mov cr0, eax 124 125 ; 5. jump to 32 bits code 126 jmp dword Code32Selector : 0 127 128 BACK_ENTRY_SEGMENT: 129 mov ax, cs 130 mov ds, ax 131 mov es, ax 132 mov ss, ax 133 mov sp, TopOfStack16 134 135 in al, 0x92 136 and al, 11111101b 137 out 0x92, al 138 139 sti 140 141 mov bp, HELLO_WORLD 142 mov cx, 12 143 mov dx, 0 144 mov ax, 0x1301 145 mov bx, 0x0007 146 int 0x10 147 148 jmp $ 149 150 ; esi --> code segment label 151 ; edi --> descriptor label 152 InitDescItem: 153 push eax 154 155 mov eax, 0 156 mov ax, cs 157 shl eax, 4 158 add eax, esi 159 mov word [edi + 2], ax 160 shr eax, 16 161 mov byte [edi + 4], al 162 mov byte [edi + 7], ah 163 164 pop eax 165 166 ret 167 168 169 [section .16] 170 [bits 16] 171 CODE16_SEGMENT: 172 mov ax, UpdateSelector 173 mov ds, ax 174 mov es, ax 175 mov fs, ax 176 mov gs, ax 177 mov ss, ax 178 179 mov eax, cr0 180 and al, 11111110b 181 mov cr0, eax 182 183 BACK_TO_REAL_MODE: 184 jmp 0 : BACK_ENTRY_SEGMENT 185 186 Code16SegLen equ $ - CODE16_SEGMENT 187 188 189 [section .s32] 190 [bits 32] 191 CODE32_SEGMENT: 192 mov ax, VideoSelector 193 mov gs, ax 194 195 mov ax, Stack32Selector 196 mov ss, ax 197 198 mov eax, TopOfStack32 199 mov esp, eax 200 201 mov ax, Data32Selector 202 mov ds, ax 203 204 mov ebp, DTOS_OFFSET 205 mov bx, 0x0C 206 mov dh, 12 207 mov dl, 33 208 209 call PrintString 210 211 mov ebp, HELLO_WORLD_OFFSET 212 mov bx, 0x0C 213 mov dh, 13 214 mov dl, 30 215 216 call PrintString 217 218 mov ax, TaskALdtSelector 219 lldt ax 220 221 jmp TaskACode32Selector : 0 222 223 ;jmp Code16Selector : 0 224 225 ; ds:ebp --> string address 226 ; bx --> attribute 227 ; dx --> dh : row, dl : col 228 PrintString: 229 push ebp 230 push eax 231 push edi 232 push cx 233 push dx 234 235 print: 236 mov cl, [ds:ebp] 237 cmp cl, 0 238 je end 239 mov eax, 80 240 mul dh 241 add al, dl 242 shl eax, 1 243 mov edi, eax 244 mov ah, bl 245 mov al, cl 246 mov [gs:edi], ax 247 inc ebp 248 inc dl 249 jmp print 250 251 end: 252 pop dx 253 pop cx 254 pop edi 255 pop eax 256 pop ebp 257 258 ret 259 260 Code32SegLen equ $ - CODE32_SEGMENT 261 262 [section .gs] 263 [bits 32] 264 STACK32_SEGMENT: 265 times 1014 * 4 db 0 266 267 Stack32SegLen equ $ - STACK32_SEGMENT 268 TopOfStack32 equ Stack32SegLen - 1 269 270 271 ; ================================== 272 ; 273 ; Task A Code Segment 274 ; 275 ;=================================== 276 [section .task-a-ldt] 277 ; Task A LDT definition 278 ; 段基址 段界限 段属性 279 280 TASK_A_LDT_ENTRY: 281 TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen - 1, DA_C + DA_32 282 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen - 1, DA_DR + DA_32 283 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen - 1, DA_DRW + DA_32 284 285 TaskALdtLen equ $ - TASK_A_LDT_ENTRY 286 287 ; Task A LDT Selector 288 TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL0 289 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL0 290 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL0 291 292 [section .task-a-dat] 293 [bits 32] 294 TASK_A_DATA32_SEGMENT: 295 TASK_A_STRING db "This is Task A", 0 296 TASK_STRING_OFFSET equ TASK_A_STRING - $$ 297 298 TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT 299 300 [section .task-a-gs] 301 [bits 32] 302 TASK_A_STACK32_SEGMENT: 303 times 1024 db 0 304 305 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT 306 TaskATopOfStack32 equ TaskAStack32SegLen - 1 307 308 [section .task-a-s32] 309 [bits 32] 310 TASK_A_CODE32_SEGMENT: 311 312 mov ax, VideoSelector 313 mov gs, ax 314 315 mov ax, TaskAStack32Selector 316 mov ss, ax 317 318 mov eax, TaskATopOfStack32 319 mov esp, eax 320 321 mov ax, TaskAData32Selector 322 mov ds, ax 323 324 jmp $ 325 326 TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
从271行开始,我们定义了代表LDT段描述符表本身的段,定义了LDT下的代码段、数据段、栈段。在代码段中,我们加载了相应的段寄存器,最终程序停在原地。
在第17行为LDT段描述符表本身占用的内存增加了段描述符项,用来描述这段内存。
第35行增加了LDT段描述符表所在段的选择子。
218-221行,我们添加了三行程序,作用为加载LDT段描述符表,跳转到LDT段描述符表描述的代码段去执行。
执行结果如下:
我们在LDT描述的代码段中加入以下打印程序:
在324-329行加入了打印字符串的功能,这个PrintString函数是在全局段描述符表的代码段中定义的。
执行结果如下:
我们看到发生了CPU硬件复位。
因为PrintString函数不是属于LDT中的代码段的,我们直接调用它导致了越界,从而CPU复位。因为现在的选择子是LDT段描述符中的选择子,即使PrintString代表的偏移地址是相同的也会发生错误。
我们将打印相关的函数复制一份到LDT描述符下的代码段中,如下:
1 %include "inc.asm" 2 3 org 0x9000 4 5 jmp ENTRY_SEGMENT 6 7 [section .gdt] 8 ; GDT definition 9 ; 段基址, 段界限, 段属性 10 GDT_ENTRY : Descriptor 0, 0, 0 11 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 12 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 13 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32 14 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 15 CODE16_DESC : Descriptor 0, 0xFFFF, DA_C 16 UPDATE_DESC : Descriptor 0, 0xFFFF, DA_DRW 17 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT 18 ; GDT end 19 20 GdtLen equ $ - GDT_ENTRY 21 22 GdtPtr: 23 dw GdtLen - 1 24 dd 0 25 26 27 ; GDT Selector 28 29 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 30 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 31 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 32 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 33 Code16Selector equ (0x0005 << 3) + SA_TIG + SA_RPL0 34 UpdateSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 35 TaskALdtSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0 36 ; end of [section .gdt] 37 38 TopOfStack16 equ 0x7c00 39 40 [section .dat] 41 [bits 32] 42 DATA32_SEGMENT: 43 DTOS db "D.T.OS!", 0 44 DTOS_OFFSET equ DTOS - $$ 45 HELLO_WORLD db "Hello World!", 0 46 HELLO_WORLD_OFFSET equ HELLO_WORLD - $$ 47 48 Data32SegLen equ $ - DATA32_SEGMENT 49 50 [section .s16] 51 [bits 16] 52 ENTRY_SEGMENT: 53 mov ax, cs 54 mov ds, ax 55 mov es, ax 56 mov ss, ax 57 mov sp, TopOfStack16 58 59 mov [BACK_TO_REAL_MODE + 3], ax 60 61 ; initialize GDT for 32 bits code segment 62 mov esi, CODE32_SEGMENT 63 mov edi, CODE32_DESC 64 65 call InitDescItem 66 67 mov esi, DATA32_SEGMENT 68 mov edi, DATA32_DESC 69 70 call InitDescItem 71 72 mov esi, DATA32_SEGMENT 73 mov edi, STACK32_DESC 74 75 call InitDescItem 76 77 mov esi, CODE16_SEGMENT 78 mov edi, CODE16_DESC 79 80 call InitDescItem 81 82 mov esi, TASK_A_LDT_ENTRY 83 mov edi, TASK_A_LDT_DESC 84 85 call InitDescItem 86 87 mov esi, TASK_A_CODE32_SEGMENT 88 mov edi, TASK_A_CODE32_DESC 89 90 call InitDescItem 91 92 mov esi, TASK_A_DATA32_SEGMENT 93 mov edi, TASK_A_DATA32_DESC 94 95 call InitDescItem 96 97 mov esi, TASK_A_STACK32_SEGMENT 98 mov edi, TASK_A_STACK32_DESC 99 100 call InitDescItem 101 102 ; initialize GDT pointer struct 103 mov eax, 0 104 mov ax, ds 105 shl eax, 4 106 add eax, GDT_ENTRY 107 mov dword [GdtPtr + 2], eax 108 109 ; 1. load GDT 110 lgdt [GdtPtr] 111 112 ; 2. close interrupt 113 cli 114 115 ; 3. open A20 116 in al, 0x92 117 or al, 00000010b 118 out 0x92, al 119 120 ; 4. enter protect mode 121 mov eax, cr0 122 or eax, 0x01 123 mov cr0, eax 124 125 ; 5. jump to 32 bits code 126 jmp dword Code32Selector : 0 127 128 BACK_ENTRY_SEGMENT: 129 mov ax, cs 130 mov ds, ax 131 mov es, ax 132 mov ss, ax 133 mov sp, TopOfStack16 134 135 in al, 0x92 136 and al, 11111101b 137 out 0x92, al 138 139 sti 140 141 mov bp, HELLO_WORLD 142 mov cx, 12 143 mov dx, 0 144 mov ax, 0x1301 145 mov bx, 0x0007 146 int 0x10 147 148 jmp $ 149 150 ; esi --> code segment label 151 ; edi --> descriptor label 152 InitDescItem: 153 push eax 154 155 mov eax, 0 156 mov ax, cs 157 shl eax, 4 158 add eax, esi 159 mov word [edi + 2], ax 160 shr eax, 16 161 mov byte [edi + 4], al 162 mov byte [edi + 7], ah 163 164 pop eax 165 166 ret 167 168 169 [section .16] 170 [bits 16] 171 CODE16_SEGMENT: 172 mov ax, UpdateSelector 173 mov ds, ax 174 mov es, ax 175 mov fs, ax 176 mov gs, ax 177 mov ss, ax 178 179 mov eax, cr0 180 and al, 11111110b 181 mov cr0, eax 182 183 BACK_TO_REAL_MODE: 184 jmp 0 : BACK_ENTRY_SEGMENT 185 186 Code16SegLen equ $ - CODE16_SEGMENT 187 188 189 [section .s32] 190 [bits 32] 191 CODE32_SEGMENT: 192 mov ax, VideoSelector 193 mov gs, ax 194 195 mov ax, Stack32Selector 196 mov ss, ax 197 198 mov eax, TopOfStack32 199 mov esp, eax 200 201 mov ax, Data32Selector 202 mov ds, ax 203 204 mov ebp, DTOS_OFFSET 205 mov bx, 0x0C 206 mov dh, 12 207 mov dl, 33 208 209 call PrintString 210 211 mov ebp, HELLO_WORLD_OFFSET 212 mov bx, 0x0C 213 mov dh, 13 214 mov dl, 30 215 216 call PrintString 217 218 mov ax, TaskALdtSelector 219 lldt ax 220 221 jmp TaskACode32Selector : 0 222 223 ;jmp Code16Selector : 0 224 225 226 ; ds:ebp --> string address 227 ; bx --> attribute 228 ; dx --> dh : row, dl : col 229 PrintString: 230 push ebp 231 push eax 232 push edi 233 push cx 234 push dx 235 236 print: 237 mov cl, [ds:ebp] 238 cmp cl, 0 239 je end 240 mov eax, 80 241 mul dh 242 add al, dl 243 shl eax, 1 244 mov edi, eax 245 mov ah, bl 246 mov al, cl 247 mov [gs:edi], ax 248 inc ebp 249 inc dl 250 jmp print 251 252 end: 253 pop dx 254 pop cx 255 pop edi 256 pop eax 257 pop ebp 258 259 ret 260 261 Code32SegLen equ $ - CODE32_SEGMENT 262 263 [section .gs] 264 [bits 32] 265 STACK32_SEGMENT: 266 times 1014 * 4 db 0 267 268 Stack32SegLen equ $ - STACK32_SEGMENT 269 TopOfStack32 equ Stack32SegLen - 1 270 271 272 ; ================================== 273 ; 274 ; Task A Code Segment 275 ; 276 ;=================================== 277 [section .task-a-ldt] 278 ; Task A LDT definition 279 ; 段基址 段界限 段属性 280 281 TASK_A_LDT_ENTRY: 282 TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen - 1, DA_C + DA_32 283 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen - 1, DA_DR + DA_32 284 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen - 1, DA_DRW + DA_32 285 286 TaskALdtLen equ $ - TASK_A_LDT_ENTRY 287 288 ; Task A LDT Selector 289 TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL0 290 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL0 291 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL0 292 293 [section .task-a-dat] 294 [bits 32] 295 TASK_A_DATA32_SEGMENT: 296 TASK_A_STRING db "This is Task A", 0 297 TASK_A_STRING_OFFSET equ TASK_A_STRING - $$ 298 299 TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT 300 301 [section .task-a-gs] 302 [bits 32] 303 TASK_A_STACK32_SEGMENT: 304 times 1024 db 0 305 306 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT 307 TaskATopOfStack32 equ TaskAStack32SegLen - 1 308 309 [section .task-a-s32] 310 [bits 32] 311 TASK_A_CODE32_SEGMENT: 312 313 mov ax, VideoSelector 314 mov gs, ax 315 316 mov ax, TaskAStack32Selector 317 mov ss, ax 318 319 mov eax, TaskATopOfStack32 320 mov esp, eax 321 322 mov ax, TaskAData32Selector 323 mov ds, ax 324 325 mov ebp, TASK_A_STRING_OFFSET 326 mov bx, 0x0c 327 mov dh, 14 328 mov dl, 29 329 330 call TaskAPrintString 331 332 jmp Code16Selector : 0 333 334 ; ds:ebp --> string address 335 ; bx --> attribute 336 ; dx --> dh : row, dl : col 337 TaskAPrintString: 338 push ebp 339 push eax 340 push edi 341 push cx 342 push dx 343 344 task_print: 345 mov cl, [ds:ebp] 346 cmp cl, 0 347 je task_end 348 mov eax, 80 349 mul dh 350 add al, dl 351 shl eax, 1 352 mov edi, eax 353 mov ah, bl 354 mov al, cl 355 mov [gs:edi], ax 356 inc ebp 357 inc dl 358 jmp task_print 359 360 task_end: 361 pop dx 362 pop cx 363 pop edi 364 pop eax 365 pop ebp 366 367 ret 368 369 TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
我们复制了打印函数,在第332行跳转到16位的保护模式,进一步跳转到16位实模式。
执行结果如下:
小结:
局部段描述符表用于组织功能相关的段
局部段描述符表需要加载后才能正常使用
局部段描述符表必须在全局段描述符表中注册
通过局部段描述符表的选择子对其进行访问
局部段描述符表时实现多任务的基础