保护模式 学习笔记--LDT

来源:互联网 发布:端口映射软件哪个好 编辑:程序博客网 时间:2024/05/07 12:52

LDT(Local Descriptor Table)它和GDT差不多,都是描述符表(Descriptor Table)区别仅仅在于全局(Global)和局部(Local)的不同。局部描述符表可以有若干张,每个任务可以有一张。

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

[cpp] view plain copy
  1. ; ==========================================  
  2. ; pmtest3.asm  
  3. ; 编译方法:nasm pmtest3.asm -o pmtest3.com  
  4. ; ==========================================  
  5.   
  6. ;常量, 宏, 以及一些说明  
  7. DA_32       EQU    4000h   ; 32 位段    
  8. DA_C        EQU    98h     ; 存在的只执行代码段属性值    
  9. DA_LDT      EQU    82h     ; 局部描述符表段类型值  
  10. DA_DRW      EQU    92h     ; 存在的可读写数据段属性值    
  11. DA_DRWA     EQU    93h     ; 存在的已访问可读写数据段类型值    
  12. ATCE32      EQU    4098h   ;存在的只执行32代码段属性值    
  13. DA_DPL1     EQU    20h     ; DPL = 1  
  14.     
  15. SA_TIG      EQU 0      ;将TI位置0   
  16. SA_TIL      EQU 4      ;将TI位置1  
  17. ;下面是宏定义    
  18. ; 有三个参数:段界限,段基址,段属性其中宏定义中的%1代表参数1,%2代表参数2,%3代表参数3    
  19. %macro Descriptor 3      
  20.     
  21.     dw  %2 & 0FFFFh             ; 段界限1(参数2的低16位)    
  22.     dw  %1 & 0FFFFh             ; 段基址1(参数1的低16位)    
  23.     db  (%1 >> 16) & 0FFh       ; 段基址2(参数1的16-23位)    
  24.     dw  ((%2 >> 8) & 0F00h) | (%3 & 0F000h)| (%3 & 000FFh) ; 属性1(高4位) + 段界限2(高4位) + 属性2(低8位)    
  25.     db  (%1 >> 24) & 0FFh       ; 段基址3(参数1的24-31位)    
  26. %endmacro ; 共 8 字节    
  27. ;段界限共20位,段基地址30位,段属性共16位(含段界限高4位)    
  28. org 0100h  
  29.     jmp LABEL_BEGIN  
  30.   
  31. [SECTION .gdt]  
  32. ; GDT  
  33. ;                                         段基址,       段界限     , 属性  
  34. LABEL_GDT:         Descriptor       0,                 0, 0         ; 空描述符  
  35. LABEL_DESC_NORMAL: Descriptor       0,            0ffffh, DA_DRW    ; Normal 描述符  
  36. LABEL_DESC_CODE32: Descriptor       0,  SegCode32Len - 1, DA_C + DA_32  ; 非一致代码段, 32  
  37. LABEL_DESC_CODE16: Descriptor       0,            0ffffh, DA_C      ; 非一致代码段, 16  
  38. LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1; Data  
  39. LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位  
  40. LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT    ; LDT  
  41. LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW    ; 显存首地址  
  42. ; GDT 结束  
  43.   
  44. GdtLen      equ $ - LABEL_GDT   ; GDT长度  
  45. GdtPtr      dw  GdtLen - 1  ; GDT界限  
  46.         dd  0       ; GDT基地址  
  47.   
  48. ; GDT 选择子  
  49. SelectorNormal      equ LABEL_DESC_NORMAL   - LABEL_GDT  
  50. SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT  
  51. SelectorCode16      equ LABEL_DESC_CODE16   - LABEL_GDT  
  52. SelectorData        equ LABEL_DESC_DATA     - LABEL_GDT  
  53. SelectorStack       equ LABEL_DESC_STACK    - LABEL_GDT  
  54. SelectorLDT     equ LABEL_DESC_LDT      - LABEL_GDT  
  55. SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT  
  56. ; END of [SECTION .gdt]  
  57.   
  58. [SECTION .data1]     ; 数据段  
  59. ALIGN   32  
  60. [BITS   32]  
  61. LABEL_DATA:  
  62. SPValueInRealMode   dw  0  
  63. ; 字符串  
  64. PMMessage:      db  "In Protect Mode now. ^-^", 0   ; 进入保护模式后显示此字符串  
  65. OffsetPMMessage     equ PMMessage - $$                  ;PMMessage起始地址偏移  
  66. PMMessage2:     db  "hello world!"  
  67. PMMessage2Len       equ     $ - PMMessage2  
  68.   
  69.   
  70. OffsetPMMessage2    equ PMMessage2 - $$  
  71.   
  72.   
  73. DataLen         equ $ - LABEL_DATA  
  74. ; END of [SECTION .data1]  
  75.   
  76.   
  77. ; 全局堆栈段  
  78. [SECTION .gs]  
  79. ALIGN   32  
  80. [BITS   32]  
  81. LABEL_STACK:  
  82.     times 512 db 0    ;栈大小  
  83. TopOfStack  equ $ - LABEL_STACK - 1   ;栈顶指针  
  84.   
  85. ; END of [SECTION .gs]  
  86.   
  87.   
  88. [SECTION .s16]  
  89. [BITS   16]  
  90. LABEL_BEGIN:  
  91.     mov ax, cs  
  92.     mov ds, ax  
  93.     mov es, ax  
  94.     mov ss, ax  
  95.     mov sp, 0100h  
  96.   
  97.     mov [LABEL_GO_BACK_TO_REAL+3], ax;如果不懂请看前面文章分析  
  98.     mov [SPValueInRealMode], sp  
  99.   
  100.     ; 初始化 16 位代码段描述符  
  101.     mov ax, cs  
  102.     movzx   eax, ax ;零扩展指令  
  103.     shl eax, 4  ;段值*16   
  104.     add eax, LABEL_SEG_CODE16               ;段值*16+偏移=16位代码段基地址   
  105.     mov word [LABEL_DESC_CODE16 + 2], ax    ;基地址1    
  106.     shr eax, 16  
  107.     mov byte [LABEL_DESC_CODE16 + 4], al    ;基地址2  
  108.     mov byte [LABEL_DESC_CODE16 + 7], ah    ;基地址3  
  109.   
  110.     ; 初始化 32 位代码段描述符  
  111.     xor eax, eax  
  112.     mov ax, cs  
  113.     shl eax, 4   ;段值*16   
  114.     add eax, LABEL_SEG_CODE32  ;段值*16+偏移=16位代码段基地址   
  115.     mov word [LABEL_DESC_CODE32 + 2], ax  ;基地址1  
  116.     shr eax, 16  
  117.     mov byte [LABEL_DESC_CODE32 + 4], al   ;基地址2  
  118.     mov byte [LABEL_DESC_CODE32 + 7], ah   ;基地址3  
  119.   
  120.     ; 初始化数据段描述符  
  121.     xor eax, eax  
  122.     mov ax, ds  
  123.     shl eax, 4   ;段值*16   
  124.     add eax, LABEL_DATA  ;段值*16+偏移=16位代码段基地址   
  125.     mov word [LABEL_DESC_DATA + 2], ax   ;基地址1  
  126.     shr eax, 16  
  127.     mov byte [LABEL_DESC_DATA + 4], al   ;基地址2  
  128.     mov byte [LABEL_DESC_DATA + 7], ah   ;基地址3  
  129.   
  130.     ; 初始化堆栈段描述符  
  131.     xor eax, eax  
  132.     mov ax, ds  
  133.     shl eax, 4   ;段值*16   
  134.     add eax, LABEL_STACK  ;段值*16+偏移=16位代码段基地址   
  135.     mov word [LABEL_DESC_STACK + 2], ax  
  136.     shr eax, 16  
  137.     mov byte [LABEL_DESC_STACK + 4], al  
  138.     mov byte [LABEL_DESC_STACK + 7], ah  
  139.   
  140.     ; 初始化 LDT 在 GDT 中的描述符  
  141.     xor eax, eax  
  142.     mov ax, ds  
  143.     shl eax, 4  ;段值*16  
  144.     add eax, LABEL_LDT  ;段值*16+偏移=16位代码段基地址 (LDT表基地址)  
  145.     mov word [LABEL_DESC_LDT + 2], ax ;基地址1  
  146.     shr eax, 16  
  147.     mov byte [LABEL_DESC_LDT + 4], al  ;基地址2  
  148.     mov byte [LABEL_DESC_LDT + 7], ah  ;基地址3  
  149.   
  150.     ; 初始化 LDT 中的描述符  
  151.     xor eax, eax  
  152.     mov ax, ds  
  153.     shl eax, 4   ;段值*16   
  154.     add eax, LABEL_CODE_A  ;段值*16+偏移=16位代码段基地址 (LDT表对应的代码段的基地址)  
  155.     mov word [LABEL_LDT_DESC_CODEA + 2], ax  ;基地址1  
  156.     shr eax, 16  
  157.     mov byte [LABEL_LDT_DESC_CODEA + 4], al  ;基地址2  
  158.     mov byte [LABEL_LDT_DESC_CODEA + 7], ah  ;基地址3  
  159.   
  160.     xor eax, eax  
  161.     mov ax, ds  
  162.     shl eax, 4   ;段值*16   
  163.     add eax, LABEL_CODE_B  ;段值*16+偏移=16位代码段基地址 (LDT表对应的代码段的基地址)  
  164.     mov word [LABEL_LDT_DESC_CODEB + 2], ax  ;基地址1  
  165.     shr eax, 16  
  166.     mov byte [LABEL_LDT_DESC_CODEB + 4], al  ;基地址2  
  167.     mov byte [LABEL_LDT_DESC_CODEB + 7], ah  ;基地址3  
  168.   
  169.     ; 为加载 GDTR 作准备  
  170.     xor eax, eax  
  171.     mov ax, ds  
  172.     shl eax, 4  ;基地址2  
  173.     add eax, LABEL_GDT      ; eax <- gdt 基地址  
  174.     mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址  
  175.   
  176.     ; 加载 GDTR  
  177.     lgdt    [GdtPtr]  
  178.   
  179.     ; 关中断  
  180.     cli  
  181.   
  182.     ; 打开地址线A20  
  183.     in  al, 92h  
  184.     or  al, 00000010b  
  185.     out 92h, al  
  186.   
  187.     ; 准备切换到保护模式  
  188.     mov eax, cr0  
  189.     or  eax, 1  
  190.     mov cr0, eax  
  191.   
  192.     ; 真正进入保护模式  
  193.     jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处  
  194.   
  195. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
  196.   
  197. LABEL_REAL_ENTRY:       ; 从保护模式跳回到实模式就到了这里  
  198.     mov ax, cs  
  199.     mov ds, ax  
  200.     mov es, ax  
  201.     mov ss, ax  
  202.   
  203.     mov sp, [SPValueInRealMode]  
  204.   
  205.     in  al, 92h     ; ┓  
  206.     and al, 11111101b   ; ┣ 关闭 A20 地址线  
  207.     out 92h, al     ; ┛  
  208.   
  209.     sti         ; 开中断  
  210.   
  211.     mov ax, 4c00h   ; ┓  
  212.     int 21h     ; ┛回到 DOS  
  213. ; END of [SECTION .s16]  
  214.   
  215.   
  216. [SECTION .s32]; 32 位代码段. 由实模式跳入.  
  217. [BITS   32]  
  218.   
  219. LABEL_SEG_CODE32:  
  220.     mov ax, SelectorData  
  221.     mov ds, ax          ; 数据段选择子  
  222.     mov ax, SelectorVideo  
  223.     mov gs, ax          ; 视频段选择子  
  224.   
  225.     mov ax, SelectorStack  
  226.     mov ss, ax          ; 堆栈段选择子  
  227.   
  228.     mov esp, TopOfStack  
  229.   
  230.   
  231.     ; 下面显示一个字符串  
  232.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  233.     xor esi, esi  
  234.     xor edi, edi  
  235.     mov esi, OffsetPMMessage    ; 源数据偏移  
  236.     mov edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。  
  237.     cld    ;设置数据传输方向  
  238. .1:  
  239.     lodsb                 ;从[ds:esi]中去一个字符送入al    
  240.     test    al, al        ;判断是否到字符串末尾(以0结尾)    
  241.     jz  .2            ;是->跳转    
  242.     mov [gs:edi], ax  ;否->输入到显存  
  243.     add edi, 2        ;指针加2(一个字符占两个字节)  
  244.     jmp .1  
  245. .2: ; 显示完毕  
  246.   
  247.     call    DispReturn    ;换行    
  248.   
  249.     ; Load LDT  
  250.     mov ax, SelectorLDT     
  251.     lldt    ax      ;加载LDT在GDT中的描述符  
  252.   
  253.   
  254.         call    SelectorLDTCodeB:0      ; 跳入局部任务  
  255.     push    SelectorLDTCodeA  
  256.     xor eax,eax  
  257.     push eax  
  258.     retf    ; 跳入局部任务  
  259.     ;jmp    SelectorLDTCodeA:0    
  260. ; ------------------------------------------------------------------------  
  261. DispReturn:  
  262.     push    eax  
  263.     push    ebx  
  264.     mov eax, edi ;获取当前光标的显存偏移    
  265.     mov bl, 160  ;每行80字符,每个字符占两个字节(字符ASCII码+字符属性),所以一行共80*2=160个字节    
  266.     div bl       ;获取当前行数(在显存中行数从0开始编号的)  
  267.     and eax, 0FFh ;取低8位(在当前页,否则有可能输出到其它页了,显示器不会显示的)  
  268.     inc eax      ;下一行  
  269.     mov bl, 160  
  270.     mul bl       ;确定下一行开始的字节数  
  271.     mov edi, eax ;更新edi寄存器  
  272.     pop ebx  
  273.     pop eax  
  274.   
  275.     ret  
  276. ; DispReturn 结束---------------------------------------------------------  
  277.   
  278. SegCode32Len    equ $ - LABEL_SEG_CODE32  
  279. ; END of [SECTION .s32]  
  280.   
  281.   
  282. ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式  
  283. [SECTION .s16code]  
  284. ALIGN   32  
  285. [BITS   16]  
  286. LABEL_SEG_CODE16:  
  287.     ; 跳回实模式:  
  288.     mov ax, SelectorNormal  ;设置实模式下的数据段  
  289.     mov ds, ax  
  290.     mov es, ax  
  291.     mov fs, ax  
  292.     mov gs, ax  
  293.     mov ss, ax  
  294.   
  295.     mov eax, cr0  
  296.     and al, 11111110b;设置cr0的0位(PE位,PE=0准备进入实模式)    
  297.     mov cr0, eax     ;更新cr0  
  298.   
  299. LABEL_GO_BACK_TO_REAL:  
  300.     jmp 0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值  
  301.   
  302. Code16Len   equ $ - LABEL_SEG_CODE16  
  303.   
  304. ; END of [SECTION .s16code]  
  305.   
  306.   
  307. ; LDT  
  308. [SECTION .ldt]  
  309. ALIGN   32  
  310. LABEL_LDT:  
  311. ;                            段基址       段界限      属性  
  312. LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位  
  313. LABEL_LDT_DESC_CODEB: Descriptor 0, CodeBLen - 1, DA_C + DA_32 ; Code, 32 位  
  314. LDTLen      equ $ - LABEL_LDT ;LDT表长度  
  315.   
  316. ; LDT 选择子  
  317. SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL  
  318. SelectorLDTCodeB    equ LABEL_LDT_DESC_CODEB    - LABEL_LDT + SA_TIL  
  319. ; END of [SECTION .ldt]  
  320.   
  321.   
  322. ; CodeA (LDT, 32 位代码段)  
  323. [SECTION .la]  
  324. ALIGN   32  
  325. [BITS   32]  
  326. LABEL_CODE_A:  
  327.     mov ax, SelectorVideo  
  328.     mov gs, ax          ; 视频段选择子(目的)  
  329.   
  330.     mov edi, (80 * 12 + 0) * 2  ; 屏幕第 12 行, 第 0 列。  
  331.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  332.     mov al, 'L'  
  333.     mov [gs:edi], ax  
  334.   
  335.     ; 准备经由16位代码段跳回实模式  
  336.     jmp SelectorCode16:0  
  337. CodeALen    equ $ - LABEL_CODE_A  
  338. ; END of [SECTION .la]  
  339.   
  340. ; CodeB (LDT, 32 位代码段)  
  341. [SECTION .lb]  
  342. ALIGN   32  
  343. [BITS   32]  
  344. LABEL_CODE_B:  
  345.     mov ax, SelectorVideo  
  346.     mov gs, ax          ; 视频段选择子(目的)  
  347.     mov     ax,SelectorData;  
  348.     mov     ds,ax  
  349.   
  350.     mov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写  
  351.       
  352.     mov     ecx,PMMessage2Len  
  353.     mov edi, (80 * 11 + 0) * 2  ; 屏幕第 11 行, 第 0 列。  
  354.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  355.  .loop  mov     al,[ds:esi]  
  356.     mov [gs:edi], ax  
  357.     add     edi,2  
  358.     inc esi  
  359.     loop .loop  
  360.     retf     
  361. CodeBLen    equ $ - LABEL_CODE_B  
  362. ; END of [SECTION .lb]  

在这里有两个局部任务,分别对应两个ldt表,一个局部任务是输出字符‘L’,一个任务是输出‘hello world!’,当然还有一个全局任务,输入字符‘In Protect Mode now.’程序先在实模式下初始化GDT,LDT表,设置相关寄存器,进入保护模式,输出一个字符串,然后加载局部描述表LDT,进入局部任务B,执行后返回,然后执行任务A,任务A完成后跳入16位代码段,设置相关寄存器值,回到实模式,结束回到DOS.

在进入保护模式前,我们需要初始化GDT,LDT表,因为在保护模式下需要根据相应段的表,找到代码或者数据的位置,当有局部任务时,GDT中应该包含LDT表存放的位置,这样当处理器在段描述符表中找到LDT描述表后发现是局部任务,这样就可以通过其记录的LDT表的位置,找到LDT表,然后在LDT表中找到相应的段描述符,如下图所示:


段描述符表=全局任务(GDT+LDT)GDTR寄存器中用于存放全局描述符表GDT的32位线性基地址和16位的表的长度值。LDTR寄存器中用于存放局部描述符表LDT的32位线性基地址和16位的表的长度值。通过系统指令,lgdt将GDT的线性基址和长度值加载到GDTR寄存器中,lldt将LDT的线性基址和长度值加载到LDTR寄存器中。

下面解释部分代码:

[cpp] view plain copy
  1. call    SelectorLDTCodeB:0      ; 跳入局部任务  
通过call 进入局部任务B(段间转移),完成相应字符输出。
[cpp] view plain copy
  1. LABEL_CODE_B:  
  2.     mov ax, SelectorVideo  
  3.     mov gs, ax          ; 视频段选择子(目的)  
  4.     mov     ax,SelectorData;  
  5.     mov     ds,ax  
  6.   
  7.     mov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写  
  8.       
  9.     mov     ecx,PMMessage2Len  
  10.     mov edi, (80 * 11 + 0) * 2  ; 屏幕第 11 行, 第 0 列。  
  11.     mov ah, 0Ch         ; 0000: 黑底    1100: 红字  
  12.  .loop  mov     al,[ds:esi]  
  13.     mov [gs:edi], ax  
  14.     add     edi,2  
  15.     inc esi  
  16.     loop .loop  
  17.     retf   
任务B中初始化相应段寄存器,设置字符串指针请注意下面语句:

[cpp] view plain copy
  1. mov     esi,OffsetPMMessage2    ;mov     esi,PMMessage2注意不能这样写  
不能用后面注释的语句,后面的语句实在实模式下的偏移地址,在保护模式中用的是相对于当前节(SECTION)的偏移地址,这个在前面也介绍过,可以参考前面文章。
任务B完成后通过retf返回(还原段选择子和偏移)。接下来准备进入任务A

[cpp] view plain copy
  1.        push    SelectorLDTCodeA  
  2. xor eax,eax  
  3. push eax  
  4. retf    ; 跳入局部任务  
  5. ;jmp    SelectorLDTCodeA:0</span>  
这里模拟是call—retf指令,把段选择子和偏移压入栈中,通过retf用栈中的数据设置cs,ip,达到跳转的目的。这就是两个局部任务A和B。

下面这段代码是在LDT表中"声明"任务A,B,分别指向相应代码段,然后设置相应选择子:

[cpp] view plain copy
  1. ; LDT  
  2. [SECTION .ldt]  
  3. ALIGN   32  
  4. LABEL_LDT:  
  5. ;                            段基址       段界限      属性  
  6. LABEL_LDT_DESC_CODEA: Descriptor 0, CodeALen - 1, DA_C + DA_32 ; Code, 32 位  
  7. LABEL_LDT_DESC_CODEB: Descriptor 0, CodeBLen - 1, DA_C + DA_32 ; Code, 32 位  
  8. LDTLen      equ $ - LABEL_LDT ;LDT表长度  
  9.   
  10. ; LDT 选择子  
  11. SelectorLDTCodeA    equ LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL  
  12. SelectorLDTCodeB    equ LABEL_LDT_DESC_CODEB    - LABEL_LDT + SA_TIL  
  13. ; END of [SECTION .ldt]  
纯DOS下运行结果:
0 0