卓一笔记---局部描述符表(LDT寻址表)的使用

      知识是浩瀚的海洋,一人穷其一生也不可能学完.但是每个人都可以像令狐冲/乔峰一样学一门独门绝技,驰骋江湖.汇编就是这样一门武学,静静修之,可使内功与日俱增.今天要讲的是LDT寻址表.

      GDT,LDT表都是32位系统用来寻址用的.GDT是全局的,LDT是局部的.当有局部任务时,GDT中应该包含LDT表存放的位置,这样当处理器在段描述符表中找到LDT描述表后发现是局部任务,这样就可以通过其记录的LDT表的位置,找到LDT表,然后在LDT表中找到相应的段描述符,如下图所示:

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

     下面是一段实践代码:

1,构建pm.inc文件


DA_LDT EQU 82h;局部描述符表段类型值
DA_32  EQU 4000h ;32位段
DA_DRW    EQU  92h  ; 存在的可读写数据段属性值
DA_DRWA    EQU  93h  ; 存在的已访问可读写数据段类型值
DA_C  EQU  98h  ;存在的只执行代码段属性

DA_DPL1    EQU    20h  ; DPL = 1

SA_TIG    EQU  0  ; ┓TI
SA_TIL    EQU  4  ; ┛

;描述符定义,传进来的参数会自动装到对应的字节中
;usage:Descriptor Base,Limit,Attr
%macro Descriptor 3
       dw %2&0FFFFh ;段界限1
       dw %1&0FFFFh ;段基址1
       db (%1>>16)&0FFh ;段基址2
       dw ((%2>>8)&0F00h)|(%3&0F0FFh) ;属性1 + 段界限2 + 属性2
       db (%1>>24)&0FFh ;段基址3
%endmacro ;共8个字节

2.构建pmldt.asm文件

; ==========================================
; pmldt.asm
; 编译方法:make -f MakeFileLDT
; ==========================================

%include "pm.inc" ;常量,宏,以及一些说明
org 0100h
jmp LABEL_BEGIN  ;跳到开始处执行,此时不会修改cs,cs仍然为0,偏移地址会加上7c00
[SECTION .gdt] ;定义GDT全局描述符号(为(段地址,段界限,属性)三元组,占8字节空间)
LABEL_GDT: Descriptor 0,0,0 ;空描述符  
LABEL_DESC_NORMAL:Descriptor 0, 0ffffh,DA_DRW ;Normal描述符
LABEL_DESC_CODE32:Descriptor 0,SegCode32Len-1,DA_C+DA_32 ;指向32位保护模式非一致代码段
LABEL_DESC_CODE16:Descriptor 0, 0ffffh,DA_C ;指向16位,非一致代码
LABEL_DESC_DATA:Descriptor 0, DataLen-1, DA_DRW+DA_DPL1 ;Data 用来验证保护模式修改了这个值
LABEL_DESC_STACK:Descriptor 0,TopOfStack, DA_DRWA+DA_32 ;stack,32位,用来存储函数调用时暂时存入的参数
LABEL_DESC_LDT:Descriptor 0, LDTLen - 1, DA_LDT  ;LDT基地址描述符
LABEL_DESC_VIDEO:Descriptor 0B8000h,0ffffh,DA_DRW ;显存(显卡)首地址描述符
;GDT结束
GdtLen equ $-LABEL_GDT ;GDT长度
GdtPtr dw GdtLen-1 ;GDT界限
       dd 0   ;GDT基地址
;GDT选择子
SelectorNormal equ LABEL_DESC_NORMAL-LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32-LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16-LABEL_GDT
SelectorData equ LABEL_DESC_DATA-LABEL_GDT
SelectorStack equ LABEL_DESC_STACK-LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO-LABEL_GDT
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT  ;选择子指向LDT基址
;这里比较特殊,各个描述符号的最右侧3位都为0,因此相减后,就是描述符在GDT表中的索引
;END of  [SECTION .gdt]

[SECTION .data1]
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0 ;从保护模式跳入实模式时,用来当做栈偏移地址
PMMessage:     db  "In Protect Mode now, ^_^",0 ;在保护模式中显示
OffsetPMMessage equ PMMessage-$$ ;保护模式下相对于选择子基址的偏移量
DataLen   equ $-LABEL_DATA
;END of [SECTION .data1]

;全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
  times 512 db 0
  TopOfStack equ $-LABEL_STACK-1 ;相对于堆栈选择子基址的字节偏移,指向512字节末尾字节
;END of [SECTION .gs]

[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
   mov ax,cs
   mov ds,ax
   mov es,ax
   mov ss,ax
   mov sp,0100h

   mov [LABEL_GO_BACK_TO_REAL+3],ax  ;由保护模式跳回来,跳到这个基地址
   mov [SPValueInRealMode],sp

   ; 初始化数据段描述符
   xor  eax, eax
   mov  ax, ds
   shl  eax, 4
   add  eax, LABEL_DATA
   mov  word [LABEL_DESC_DATA + 2], ax
   shr  eax, 16
   mov  byte [LABEL_DESC_DATA + 4], al
   mov  byte [LABEL_DESC_DATA + 7], ah
   
   ;初始化LDT在GDT中的描述符,对准LDT表的基址
   xor eax,eax
   mov ax, ds
   shl eax, 4  ;注意偏移地址时这里一定时eax,要不然20位地址就溢出了
   add eax,LABEL_LDT
   mov word [LABEL_DESC_LDT+2],ax
   shr eax,16
   mov byte [LABEL_DESC_LDT+4],al
   mov byte [LABEL_DESC_LDT+7],ah


   ; 初始化堆栈段描述符
    xor  eax, eax
    mov  ax, ds
    shl  eax, 4
    add  eax, LABEL_STACK
    mov  word [LABEL_DESC_STACK + 2], ax
    shr  eax, 16
    mov  byte [LABEL_DESC_STACK + 4], al
    mov  byte [LABEL_DESC_STACK + 7], ah

  ;初始化32位代码段全局描述符的段基址
   xor eax,eax
   mov ax,cs
   shl eax,4
   add eax,LABEL_SEG_CODE32
   mov word [LABEL_DESC_CODE32+2],ax
   shr eax,16
   mov byte [LABEL_DESC_CODE32+4],al
   mov byte [LABEL_DESC_CODE32+7],ah
   
   ;初始化LDT中的描述符
   xor eax,eax
   mov ax,ds
   shl eax,4
   add eax,LABEL_CODE_A
   mov word [LABEL_LDT_DESC_CODEA+2],ax
   shr eax,16
   mov byte [LABEL_LDT_DESC_CODEA+4],al
   mov byte [LABEL_LDT_DESC_CODEA+7],ah
   
   ;初始化16位代码段全局描述符的段基址
   xor eax,eax
   mov ax,cs
   shl eax,4
   add eax,LABEL_SEG_CODE16
   mov word [LABEL_DESC_CODE16+2],ax
   shr eax,16
   mov byte [LABEL_DESC_CODE16+4],al
   mov byte [LABEL_DESC_CODE16+7],ah


   ;为加载GDT表做准备,先将GDT表的界限和起始地址找到存到Gdtptr中
   xor eax,eax
   mov ax,ds
   shl eax,4
   add eax,LABEL_GDT
   mov dword [GdtPtr + 2],eax ;将GDT的基地址放好
   lgdt  [GdtPtr]  ;将GDT表加载进来,后面可以访问GDT表中的值
   ;关中断
   cli
   ;打开地址线A20
   in al,92h
   or al,00000010b
   out 92h,al
   ;准备切换到保护模式
   mov eax,cr0
   or eax,1
   mov cr0,eax

   ;真正进入保护模式
   jmp dword SelectorCode32:0 ;执行这句话,为了将SelectorCode32
                              ;指向的32为保护模式代码载入cs中
                              ;并跳转到SelectorCode32:0处
   
    
  LABEL_REAL_ENTRY:    ;从保护模式跳回到实模式会走到这里
  mov ax,cs
  mov ds,ax
  mov es,ax
  mov ss,ax
  
  mov sp,[SPValueInRealMode] 
  
  in al,92h  ;关闭A20地址线
  and al,11111101b 
  out 92h, al 
  
  sti ;打中断
 
  mov ax,4c00h ;启动21号中断,回到dos窗口 
  int 21h ;
  ;END of  [SECTION .s16]
   
   
   
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
  mov ax,SelectorData
  mov ds,ax ;数据段选择子
  
  mov ax,SelectorVideo
  mov gs,ax    ;视屏段选择子
  
  mov ax,SelectorStack
  mov ss,ax      ;堆栈选择子 

  mov esp,TopOfStack  ;把栈顶初始化,ss:esp指向栈顶

  ;下面显示一个字符
  mov ah,0ch  ;0代表黑底,c代表红字
  xor esi,esi
  xor edi,edi
  mov esi,OffsetPMMessage ;ds:esi指向Data数据区
  mov edi,(80*10+0)*2 ;目的数据偏移.屏幕第10行,第0列
  cld

.1:
  lodsb  ;ds:esi指向的数据会自动加载到al中
  test al,al ;al为0时代表没有数据了需要结束显示
  jz .2
  mov [gs:edi],ax
  add edi,2   ;显示一个字符占两个字节(颜色和字)
  jmp .1
.2:  ;显示完毕
  
  call DispReturn ;在下一行开头处写数据

  
  ;Load LDT表
  mov ax,SelectorLDT
  lldt ax
  ;跳入LDT表中局部描述符指向的任务
  jmp SelectorLDTCodeA:0

DispReturn:
   push eax
   push ebx
   mov eax,edi
   mov bl,160
   div bl  ;计算出当前行号,一行占用160字节(80个字符)
   and eax,0ffh;上一步得到的商在eax存着,取出行数
   inc eax ;让行数加1
   mov bl,160
   mul bl   ;重新计算出行首的字节处
   mov edi,eax;上一步计算出的值在eax中,让显示屏定位到下一行行首
   pop ebx
   pop eax

   ret
;DispReturn结束------
       
SegCode32Len equ $-LABEL_SEG_CODE32
;END of  [SECTION .s32]
;LDT

[SECTION .ldt]
ALIGN 32
LABEL_LDT:
;段基址 段界限  属性
LABEL_LDT_DESC_CODEA:Descriptor 0,CodeALen-1,DA_C+DA_32 ;Code,32位代码
LDTLen equ $-LABEL_LDT

;LDT选择子
SelectorLDTCodeA equ LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL ;LDT描述符指向要调用的地址,
;SA_TIL为表明描述符为LDT描述符,会将TIL位置位1,代表是LDT选择子
;END of [SECTION .ldt]

;CODEA(LDT,32位描述符)
[SECTION .la]
ALIGN 32
[BITS 32]
LABEL_CODE_A:
  mov ax, SelectorVideo
  mov gs, ax    ;视频段选择子,指向显示屏,输出信息
  
  mov edi,(80*12+0)*2 ;屏幕第12行,第0列输出信息
  mov ah,0ch  ;0是黑底,c是红色字
  mov al,'L'  ;显示屏上输出L
  mov [gs:edi],ax ;显示屏上输出
   
  ;LDT表指示的任务完成后,跳回实模式
  jmp SelectorCode16:0
CodeALen equ $-LABEL_CODE_A
;END of [SECTION .la]

[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16: ;跳回实模式
  mov ax,SelectorNormal ;回到正常16位模式段
  mov ds,ax
  mov es,ax
  mov fs,ax
  mov ss,ax

  mov eax,cr0
  mov al,11111110b
  mov cr0,eax

LABEL_GO_BACK_TO_REAL:
  jmp 0:LABEL_REAL_ENTRY ;段地址在前面会重新被置为正确的值
Code16Len  equ $-LABEL_SEG_CODE16
;END of [SECTION .s16code]

3.构建MakeFileLDT文件,用来构建项目


##################################################
# Makefile of pmtestx.asm (x=[1,2,3...])
##################################################

SRC:=pmldt.asm
BIN:=$(subst .asm,.com,$(SRC)) #${SRC}中的.asm会替换成.com
#此句等价于pmprotect2real.com

.PHONY : everything

everything : $(BIN)
  sudo mount -o loop pm.img /mnt/floppy/
  sudo cp $(BIN) /mnt/floppy/ -fv
  sudo umount /mnt/floppy/

$(BIN) : $(SRC)
  nasm $< -o $@ #等价与nasm pmldt.asm -o pmldt.com

4.运行结果如下,图上显示的L正是LDT表中的地址描述符指向的任务显示出来的.

 

源码:https://github.com/Foolegend/aos/tree/master/chapter03/LDT

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值