保护模式中的特权级 之 使用调用门描述符实现段间跳转

问题:如何在不同特权级的代码之间跳转执行?

在这里插入图片描述

调用门描述符 用于从 低特权级代码段跳转到高特权级代码段 并返回

门描述符也是描述符,结构上同 段描述符一样,也是占用8个字节。
在这里插入图片描述
调用门描述符: 调用门中保存了 某个段的选择子以及相应的偏移地址。 地址位一共32位 4个字节,正好是一个偏移地址所需要存储空间。

在这里插入图片描述
调用门描述符的工作原理:
只要是描述符,那么必然会对应有一个选择子,既然调用门描述符是一个描述符,那么就会对应一个选择子,即调用门选择子。可以使用 调用门选择子 来访问对应的门描述符, 门描述符中有两个很重要的信息,一个是选择子,一个是偏移地址。选择子指的是段描述符的选择子,所以我们可以通过这个段选择子 访问到一个对应的段描述符,拿到对应的段基地址和段界限,再使用门描述符中的记录的偏移地址,就可以得到一个确定的内存地址,就可以跳转到该地址执行。所以 通过调用门选择子 最终得到一个确定的内存地址,然后跳转到该地址,所以通过调用门选择子可以跳转到某个固定的内存地址上来执行。有点类似于 C语言中 函数指针。

在这里插入图片描述

全局段描述附表
[section .gdt]
; GDT definition
;                                 段基址,       段界限,       段属性
GDT_ENTRY       :     Descriptor    0,            0,           0
...
;定义一个专门定义函数的 代码段
FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32

调用门描述符
; Gate Descriptor
; Call Gate                        选择子,         偏移,      参数个数,      属性
;定义两个门描述符,分别对应 FUNCTION_DESC代码段中两个函数的入口地址
;该调用门描述符中保存  FUNCTION_DESC段的选择子,以及段内偏移地址(指的是该代码段中某个函数的入口地址
FUNC_CG_ADD_DESC      Gate   FunctionSelector,   CG_Add,        0,      DA_386CGate
FUNC_CG_SUB_DESC      Gate   FunctionSelector,   CG_Sub,        0,      DA_386CGate

; GDT end

;代码段 段描述符选择子
FunctionSelector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
;定义两个调用门描述符选择子  分别指向对应的函数地址,类似于C语言中的函数指针,所以通过选择子调用相应的函数
FuncCGAddSelector  equ (0x0005 << 3) + SA_TIG + SA_RPL0
FuncCGSubSelector  equ (0x0006 << 3) + SA_TIG + SA_RPL0

两个调用门描述符选择子  分别指向对应的函数地址,类似于C语言中的函数指针,所以通过调用门选择子 调用相应的函数

;call Add, Sub function
call FuncCGAddSelector   :  0  
call FuncCGSubSelector   :  0

在这里插入图片描述

在这里插入图片描述

段内跳转 call jmp 近跳转,参数是相对地址(如 跳转到距离当前位置10个字节的位置执行)
call跳转指令 就是 函数调用时的跳转,他最大的特点是 跳转过去执行之后,还能返回回来
jmp 是无条件跳转,跳转过去之后 不会返回,有去无回

段间跳转 call far, jmp far 远跳转,参数是选择子和偏移地址(即目标段基地址和段内偏移地址)

在这里插入图片描述

实验:

inc.asm

; Segment Attribute
DA_32    equ    0x4000
DA_DR    equ    0x90
DA_DRW   equ    0x92
DA_DRWA  equ    0x93
DA_C     equ    0x98
DA_CR    equ    0x9A
DA_CCO   equ    0x9C
DA_CCOR  equ    0x9E

; Segment Privilege
DA_DPL0		equ	  0x00    ; DPL = 0
DA_DPL1		equ	  0x20    ; DPL = 1
DA_DPL2		equ	  0x40    ; DPL = 2
DA_DPL3		equ	  0x60    ; DPL = 3

; Special Attribute
DA_LDT       equ    0x82
DA_TaskGate  equ    0x85	; 任务门类型值
DA_386TSS    equ	0x89	; 可用 386 任务状态段类型值
DA_386CGate  equ	0x8C	; 386 调用门类型值
DA_386IGate  equ	0x8E	; 386 中断门类型值
DA_386TGate  equ	0x8F	; 386 陷阱门类型值

; Selector Attribute
SA_RPL0    equ    0
SA_RPL1    equ    1
SA_RPL2    equ    2
SA_RPL3    equ    3

SA_TIG    equ    0
SA_TIL    equ    4

; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3	                          ; 段基址, 段界限, 段属性
    dw    %2 & 0xFFFF                         ; 段界限1
    dw    %1 & 0xFFFF                         ; 段基址1
    db    (%1 >> 16) & 0xFF                   ; 段基址2
    dw    ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2
    db    (%1 >> 24) & 0xFF                   ; 段基址3
%endmacro                                     ; 共 8 字节

; 门
; usage: Gate Selector, Offset, DCount, Attr
;        Selector:  dw
;        Offset:    dd
;        DCount:    db
;        Attr:      db
%macro Gate 4
    dw    (%2 & 0xFFFF)                      ; 偏移地址1
    dw    %1                                 ; 选择子
    dw    (%3 & 0x1F) | ((%4 << 8) & 0xFF00) ; 属性
    dw    ((%2 >> 16) & 0xFFFF)              ; 偏移地址2
%endmacro 

loader.asm

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]
; GDT definition
;                                 段基址,       段界限,       段属性
GDT_ENTRY       :     Descriptor    0,            0,           0
CODE32_DESC     :     Descriptor    0,    Code32SegLen - 1,    DA_C + DA_32
VIDEO_DESC      :     Descriptor 0xB8000,     0x07FFF,         DA_DRWA + DA_32
STACK32_DESC    :     Descriptor    0,     TopOfStack32,       DA_DRW + DA_32

;定义 函数代码段 段描述符,该代码段中都是函数
;可执行的代码段,属性 DA_C + DA_32
FUNCTION_DESC   :     Descriptor    0,   FunctionSegLen - 1,   DA_C + DA_32

; Gate Descriptor 门描述符
; Call Gate调用门描述符          选择子,         偏移,      参数个数,      属性
;定义两个调用门描述符,分别对应 FUNCTION_DESC 代码段中两个函数的入口地址
;该调用门描述符中保存  FUNCTION_DESC段的选择子,以及段内偏移地址(指的是该代码段中某个函数的入口地址
;属性 是 调用门类型值 DA_386CGate  equ	0x8C	; 386 调用门类型值
FUNC_CG_ADD_DESC      Gate   FunctionSelector,   CG_Add,        0,      DA_386CGate
FUNC_CG_SUB_DESC      Gate   FunctionSelector,   CG_Sub,        0,      DA_386CGate
; GDT end

GdtLen    equ   $ - GDT_ENTRY

GdtPtr:
          dw   GdtLen - 1
          dd   0
          
          
; GDT Selector

Code32Selector     equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector      equ (0x0002 << 3) + SA_TIG + SA_RPL0
Stack32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
;FUNCTION_DESC函数代码段 段描述符选择子
FunctionSelector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
;定义两个调用门描述符选择子  分别指向对应的函数地址,类似于C语言中的函数指针,所以通过选择子调用相应的函数
FuncCGAddSelector  equ (0x0005 << 3) + SA_TIG + SA_RPL0
FuncCGSubSelector  equ (0x0006 << 3) + SA_TIG + SA_RPL0

; end of [section .gdt]

TopOfStack16    equ 0x7c00

[section .s16]
[bits 16]
ENTRY_SEGMENT:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16
    
    ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT
    mov edi, CODE32_DESC
    
    call InitDescItem
    
    mov esi, STACK32_SEGMENT
    mov edi, STACK32_DESC
    
    call InitDescItem
    
	;初始化 段描述符表中的  FUNCTION_DESC函数代码段  的 段描述符的段基址信息
    mov esi, FUNCTION_SEGMENT
    mov edi, FUNCTION_DESC
    call InitDescItem
    
    ; initialize GDT pointer struct
    mov eax, 0
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY
    mov dword [GdtPtr + 2], eax

    ; 1. load GDT
    lgdt [GdtPtr]
    
    ; 2. close interrupt
    cli 
    
    ; 3. open A20
    in al, 0x92
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0


; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
    push eax

    mov eax, 0
    mov ax, cs
    shl eax, 4
    add eax, esi
    mov word [edi + 2], ax
    shr eax, 16
    mov byte [edi + 4], al
    mov byte [edi + 7], ah
    
    pop eax
    
    ret
    
[section .s32]
[bits 32]
CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax
    
    mov ax, Stack32Selector
    mov ss, ax
    
    mov eax, TopOfStack32
    mov esp, eax
    
	;函数调用前 参数赋值
    mov ax, 2
    mov bx, 1
    ;通过选择子调用相应的函数
	;说明 这里的0是语法需要 表示是段间跳转 0本身无意义,但是不能删除
	;如果删除后 就变成了 call FuncCGAddSelector,成了段内跳转,意义本身发生变化,所以必须留着
    call FuncCGAddSelector : 0
    call FuncCGSubSelector : 0
	
	;函数指针的本质就是函数入口地址,所以也可以这样调用函数:
	;原理就是直接使用函数地址(段基址+段内偏移地址)
	;call FunctionSelector : CG_Add
    ;call FunctionSelector : CG_Sub

    jmp $

Code32SegLen    equ    $ - CODE32_SEGMENT

;定义一个32位代码段,里面全部是函数
[section .func]
[bits 32]
FUNCTION_SEGMENT:

; ax --> a
; bx --> b
; 
; return:
;     cx --> a + b
AddFunc:
    mov cx, ax
    add cx, bx
	;返回指令 retf (ret far  远调用 远返回)
    retf
;函数  AddFunc 段内偏移  
CG_Add    equ    AddFunc - $$

; ax --> a
; bx --> b
; 
; return:
;     cx --> a - b
SubFunc:
    mov cx, ax
    sub cx, bx
	;返回指令 retf (ret far  远调用 远返回)
    retf
;函数SubFunc 段内偏移 
CG_Sub    equ    SubFunc - $$  

;代码段长度
FunctionSegLen   equ  $ - FUNCTION_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
    times 1024 * 4 db 0
    
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32  equ Stack32SegLen - 1

调用门 在使用上 就是函数指针,本质就是函数指针,但是调用门 不仅仅只是为了 实现段间跳转,实现函数调用,另一个关键的作用是实现不同特权级代码之间的跳转所以 使用前面 直接使用选择子+段内偏移地址 调用函数的方式 并不是完善的 因为这样不能实现不同特权级代码之间的跳转

;通过选择子调用相应的函数
;说明 这里的0是语法需要 表示是段间跳转 0本身无意义,但是不能删除
;如果删除后 就变成了 call FuncCGAddSelector,成了段内跳转,意义法身变化,所以必须留着
call FuncCGAddSelector : 0
call FuncCGSubSelector : 0

;函数指针的本质就是函数入口地址,所以也可以这样调用函数:
;原理就是直接使用函数地址(段基址+段内偏移地址),但是不能实现不同特权级代码之间的跳转
;call FunctionSelector : CG_Add
;call FunctionSelector : CG_Sub

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值