程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編教程:控制轉移(1)

匯編教程:控制轉移(1)

編輯:匯編語言

控制轉移基本上可分為兩大類:同一任務內的控制轉移和任務間的控制轉移(任務切換)。同一任務內的控制轉移又可分為:段內轉移、特權級不變的段間轉移和特權級變換的段間轉移。段內轉移與實模式下相似,不涉及特權級變換和任務切換。只有段間轉移才涉及特權級變換和任務切換。本文介紹保護方式下的控制轉移,重點是任務內的特權級變換和任務間的切換。

<一>任務內無特權級變換的轉移

各種段內轉移與實模式下相似,當然不涉及特權級變換和任務切換。只有各種形式的段間轉移才涉及特權級變換和任務切換。

1.段間轉移指令

與實模式下相同,指令JMP、CALL和RET都具有段間轉移的功能,指令INT和IRET總是段間轉移。此外,中斷/異常也將引起段間轉移。有時把這些具有段間轉移功能的指令統稱為段間轉移指令。

在保護模式下,段間轉移的目標位置由選擇子和偏移構成的地址表示,常把它稱為目標地址指針。在32位代碼段中,上述指針內的偏移使用32位表示,這樣的指針也稱為48位全指針。在實例二的32位代碼段內就使用了48位全指針。在16位代碼段中,上述指針內的偏移只使用16位表示。

與實模式下相似,段間轉移指令JMP和段間調用指令CALL還可分為段間直接轉移和段間間接轉移兩類。如果指令JMP和CALL在指令中直接含有目標地址指針,那麼就是段間直接轉移;如果指令中含有指向包含目標地址指針的門描述符或TSS描述符的指針,那麼就是段間間接轉移,這種指針只有選擇子部分有效,指示調用門、任務門或 TSS描述符,而偏移部分不起作用。實際上,當段間轉移指令JMP和段間調用指令CALL所含指針的選擇子部分指示代碼段描述符,那麼就是段間直接轉移,偏移部分表示目標代碼段的入口點;當選擇子部分指示門描述符或TSS描述符時,就是段間間接轉移。

2.向目標代碼段轉移的步驟

處理器在執行上述段間轉移指令向目標代碼段實施轉移的過程中,一般至少要經過如下步驟:

(1)判斷目標地址指針內的選擇子指示的描述符是否為空描述符。空描述符是GDT中的第0個描述符,是一個特殊的描述符。目標代碼段描述符不能為空描述符,也即選擇子的高14位不能為0。

(2)從全局或局部描述符表內讀出目標代碼段描述符。由選擇子內的TI位,確定使用全局描述符表還是局部描述符表。

(3)根據情況,檢測描述符類型是否正確;調整RPL。

(4)把目標代碼段描述符內的有關內容裝載到CS高速緩沖寄存器。

(5)判斷目標地址指針內的偏移是否越出代碼段的界限。目標地址指針內的偏移必須不超過目標代碼段界限。

(6)裝載CS段寄存器和指令指針寄存器EIP;CPL存入CS內選擇子的RPL字段。

上述步驟只是對轉移過程的簡單說明,實際的動作還要復雜。在把目標代碼段描述符內的有關內容轉載到CS高速緩沖寄存器時,還要進行如下保護檢測,其中的DPL表示目標代碼段描述符的特權級:

(1)對於非一致代碼段,要求CPL=DPL,RPL<=DPL;對於一致代碼段,要求CPL>=DPL。

(2)代碼段必須存在,即描述符中的P位必須是1。

通常描述符特權級DPL規定了對應段的特權級。如果描述符描述的是數據段,那麼DPL就規定了訪問該數據段的最外層特權級;如果描述符描述的是代碼段,那麼DPL就規定了執行該代碼段所需要的CPL。但從上述裝載CS高速緩沖寄存器時進行的保護檢測可見,對於一致代碼段,卻要求CPL>=DPL,也就是說,一致代碼段描述符中的DPL規定了可以轉移到一致代碼段的最內層特權級。於是,3級的程序可以轉移到任何一致的代碼段,而0級的程序只允許轉移到DPL等於0的一致代碼段。一致代碼段描述符內DPL的這種解釋,正好與正常的DPL的解釋相反。

一致的可執行段是一種特別的段。這種存儲段,為在多個特權級執行的程序,提供對子例程的共享支持,而不要求改變特權級。例如,通過把數值庫例程放在一致的代碼段中,可以使不同級執行的程序共享數值庫例程。這樣,任何特權級的程序可以使用段間調用指令,調用庫中的例程,並在調用者所具有的特權級執行該例程。

3.任務內無特權級變換的轉移

所謂任務內無特權級變換的轉移指:在轉移到新的代碼段時,當前特權級CPL保持不變。利用段間轉移指令JMP、段間調用指令CALL和段間返回指令RET可實現任務內無特權級變換的轉移。利用INT指令和IRET指令也可實現任務內無特權級變換的轉移。

(1)利用段間直接轉移指令JMP或CALL

在執行段間轉移指令JMP時,如果指令內所含指針指示一個代碼段,那麼就直接開始上述向目標代碼段轉移的步驟;在執行段間調用指令CALL時,如果指令內所含指針指針指示一個代碼段,那麼就把返回地址指針壓棧,然後就直接開始上述向目標代碼段轉移的步驟。順利通過這幾步(不調整RPL)後,就完成了任務內無特權級變換的轉移。

由此可見,利用段間直接轉移指令JMP或調用指令CALL可方便地進行任務內無特權級變換的轉移,但不能進行任務內特權級變換的轉移。

(2)利用段間返回指令RET

在執行段間返回指令RET時,如果從堆棧中彈出的目標地址指針指示一個代碼段,並且選擇子符合RPL=CPL的條件,那麼就開始上述向目標代碼段的轉移步驟。順利通過這幾步後,就完成了任務內無特權級變換的轉移。

通常情況下,段間返回指令RET與段間調用指令CALL對應。在利用段間調用指令CALL以任務內無特權級變換的方式轉移到某個子程序後,在子程序內利用段間返回指令RET以任務內無特權級變換的方式返回主程序。由於調用時無特權級變換,所以返回時也無特權級變換,如果真是如此,那麼必須能夠滿足條件RPL=CPL。

(3)利用調用門和其它途徑

如何利用調用門實行和其它方法實現任務內無特權級變換的轉移將在後面的文章中介紹。

4.裝載數據段和堆棧段寄存器時的特權檢測

上面簡單地說明了把選擇子裝入代碼段寄存器CS時為實現保護而進行的檢測,下面也簡單地說明在把選擇子裝入數據段寄存器和堆棧段寄存器時要進行的檢測。

在把選擇子裝入數據段寄存器DS、ES、FS或GS時,要進行如下檢測:

(1)選擇子不能為空;

(2)選擇子指定的描述符必須是數據段描述符、可讀可執行的代碼段或一致可讀的可執行代碼段的描述符;

(3)對於數據段和可讀可執行代碼段,要求CPL<=DPL,RPL<=DPL;

(4)對應的段必須存在。

若裝入的選擇子不滿足上述要求,則會產生異常。

在把選擇子裝入堆棧段寄存器SS時要進行如下檢測:

(1)選擇子不能為空;

(2)選擇子指定的描述符必須是可讀寫的數據段描述符;

(3)要求CPL=DPL=RPL;

(4)對應段必須存在。

若裝入的選擇子不滿足上述條件,則在裝入SS時就會引起異常。

<二>演示任務內無特權級變換轉移的實例(實例三)

在實例二中,32位代碼段到16位代碼段的轉移就是任務內無特權級轉移的例子。

下面再給出一個用於演示任務內無特權級變換轉移的實例。該實例使用了段間轉移指令JMP、段間調用指令CALL和段間返回指令RET實現同一任務內相同特權級的轉移。該實例還建立並使用了局部描述符表LDT。

1.實現步驟和源程序

實現步驟如下:(1)實模式下的初始化,包括對GDT和演示任務LDT的初始化,裝載GDTR;(2)從實模式切換到保護模式,處於0特權級;(3)裝載LDTR,並設置堆棧;(4)利用段間轉移指令JMP實現從代碼段K到同級代碼段L的轉移;(5)利用段間調用指令CALL調用同級代碼段C中的子程序D顯示字符串信息;(6)利用段間調用指令CALL調用同級代碼段C中的子程序H把十六進制數轉換成對應的ASCII碼;(7)再利用段間調用指令CALL調用同級代碼段C中的子程序D顯示字符串信息;(8)利用段間轉移指令JMP實現從代碼段L到代碼段K的轉移;(9)從保護模式切換到實模式;(10)在實模式下結束程序。

  該實例的邏輯功能是用十六進制數的形式顯示代碼段L的段界限的值。源程序如下:

;名稱:ASM3.ASM
;功能:演示任務內無特權級變換的轉移
;編譯:TASM ASM3.ASM
;連接:TLINK ASM3.OBJ
;----------------------------------------------------------------------------
INCLUDE     386SCD.INC
;----------------------------------------------------------------------------
GDTSeg     SEGMENT PARA USE16 'GDT'     ;全局描述符表數據段(16位)
;----------------------------------------------------------------------------
GDT       LABEL  BYTE           ;全局描述符表
DUMMY      Desc  <>            ;空描述符
Normal     Desc  <0ffffh,,,ATDW,,>     ;規范段描述符
CodeK      Desc  <0ffffh,,,ATCE,,>     ;代碼段K的描述符
LDTable     Desc  <LDTLen-1,,,ATLDT,,>   ;局部描述符表段的描述符
;----------------------------------------------------------------------------
GDTLen     =    $-GDT           ;全局描述符表長度
;----------------------------------------------------------------------------
Normal_Sel   =    Normal-GDT        ;規范段描述符選擇子
CodeK_Sel    =    CodeK-GDT         ;代碼段K的選擇子
LDT_Sel     =    LDTable-GDT        ;局部描述符表段的選擇子
;----------------------------------------------------------------------------
GDTSeg     ENDS               ;全局描述符表段定義結束
;----------------------------------------------------------------------------
LDTSeg     SEGMENT PARA USE16 'LDT'     ;局部描述符表數據段(16位)
LDT       LABEL  BYTE           ;局部描述符表
;代碼段L的描述符
CodeL      Desc  <CodeLLen-1,CodeLSeg,,ATCE,,>
;代碼段C的描述符
CodeC      Desc  <CodeCLen-1,CodeCSeg,,ATCE,,>
;顯示緩沖區段描述符
VideoBuf    Desc  <0ffffh,0b800h,,ATDW,,>
;LDT別名段描述符(DPL=3)
ToLDT      Desc  <LDTLen-1,LDTSEG,,ATDR+DPL3,,>
;顯示信息緩沖區數據段描述符(DPL=3)
MData      Desc  <MDataLen-1,MDataSeg,,ATDW+DPL3,,>
;堆棧段描述符
StackS     Desc  <TopOfS-1,StackSeg,,ATDWA,,>
;----------------------------------------------------------------------------
LDTLen     =    $-LDT           ;LDT所占字節數
LDNum      =    ($-LDT)/(SIZE Desc)    ;LDT含描述符項數
;----------------------------------------------------------------------------
CodeL_Sel    =    CodeL-LDT+TIL       ;代碼段L的選擇子
CodeC_Sel    =    CodeC-LDT+TIL       ;代碼段C的選擇子
Video_Sel    =    VideoBuf-LDT+TIL     ;顯示緩沖區選擇子
ToLDT_Sel    =    ToLDT-LDT+TIL       ;LDT別名段選擇子
MData_Sel    =    MData-LDT+TIL+RPL3    ;顯示信息數據段選擇子
Stack_Sel    =    StackS-LDT+TIL      ;堆棧段選擇子
;----------------------------------------------------------------------------
LDTSeg     ENDS               ;局部描述符表段定義結束
;----------------------------------------------------------------------------
MDataSeg    SEGMENT PARA USE16 'MDATA'    ;顯示信息緩沖區數據段
;----------------------------------------------------------------------------
Message     DB   'Value=',0
Buffer     DB   80 DUP(0)
MDataLen    =    $
;----------------------------------------------------------------------------
MDataSeg    ENDS               ;顯示緩沖區數據段結束
;----------------------------------------------------------------------------
StackSeg    SEGMENT DWORD USE16 'STACK'    ;堆棧段
;----------------------------------------------------------------------------
         DW   512 DUP(?)
TopOfS     =    $
;----------------------------------------------------------------------------
StackSeg    ENDS               ;堆棧段結束
;----------------------------------------------------------------------------
CodeCSeg    SEGMENT PARA USE16 'CODEC'    ;任務代碼段C
         ASSUME CS:CodeCSeg
;----------------------------------------------------------------------------
;顯示信息子程序
;入口參數:fs:si指向要顯示的以0結尾的字符串,es:di指向顯示緩沖區
;----------------------------------------------------------------------------
DispMsg     PROC  FAR
         mov   ah,01001110b
Disp1:     mov   al,BYTE PTR fs:[si]
         inc   si
         or   al,al
         jz   Disp2
         mov   WORD PTR es:[di],ax
         inc   di
         inc   di
         jmp   Disp1
Disp2:     ret
DispMsg     ENDP
;----------------------------------------------------------------------------
;把AL寄存器低4位二進制數(一位16進制數)轉換成ASCII碼
;----------------------------------------------------------------------------
HToASCII    PROC  FAR
         and   al,00001111b
         add   al,90h
         daa
         adc   al,40h
         daa
         ret
HToASCII    ENDP
;----------------------------------------------------------------------------
CodeCLen    =    $
;----------------------------------------------------------------------------
CodeCSeg    ENDS               ;代碼段C定義結束
;----------------------------------------------------------------------------
CodeLSeg    SEGMENT PARA USE16 'CODEL'
         ASSUME CS:CodeLSeg
;----------------------------------------------------------------------------
Virtual2    PROC  FAR
         mov   ax,Video_Sel       ;設置顯示緩沖區指針
         mov   es,ax
         mov   di,1986
         mov   ax,MData_Sel       ;設置提示信息緩沖區指針
         mov   fs,ax
         mov   si,OFFSET Message
         CALL16 CodeC_Sel,DispMsg     ;顯示提示信息
         mov   ax,ToLDT_Sel       ;把演示任務的LDT的別名
         mov   gs,ax           ;段的描述符選擇子裝入GS
         mov   dx,WORD PTR gs:CodeL.LimitL
         mov   si,OFFSET Buffer     ;取代碼段L的段界限值
         mov   cx,4           ;並轉成對應可顯示字符串
Vir:      rol   dx,4
         mov   al,dl
         CALL16 CodeC_Sel,HToASCII
         mov   BYTE PTR fs:[si],al
         inc   si
         loop  Vir
         mov   WORD PTR fs:[si],'H'
         mov   si,OFFSET Buffer
         CALL16 CodeC_Sel,DispMsg
         JUMP16 CodeK_Sel,Virtual3
CodeLLen    =    $
Virtual2    ENDP
;----------------------------------------------------------------------------
CodeLSeg    ENDS
;----------------------------------------------------------------------------
CodeKSeg    SEGMENT PARA USE16 'CODEK'
         ASSUME CS:CodeKSeg
;----------------------------------------------------------------------------
Virtual1    PROC  FAR
         mov   ax,LDT_Sel
         LLDT  ax            ;加載局部描述符表寄存器LDTR
         mov   ax,Stack_Sel
         mov   ss,ax           ;建立演示任務堆棧
         mov   sp,OFFSET TopOfS
         JUMP16 CodeL_Sel,Virtual2
Virtual3:    mov   ax,Normal_Sel
         mov   es,ax
         mov   fs,ax
         mov   gs,ax
         mov   ss,ax
         mov   eax,cr0
         and   al,11111110b
         mov   cr0,eax
         JUMP16 <SEG Real>,<OFFSET Real>
CodeKLen    =    $
Virtual1    ENDP
;----------------------------------------------------------------------------
CodeKSeg    ENDS
;============================================================================
RDataSeg    SEGMENT PARA USE16        ;實方式數據段
VGDTR      PDesc  <GDTLen-1,>        ;GDT偽描述符
SPVar      DW   ?             ;用於保存實方式下的SP
SSVar      DW   ?             ;用於保存實方式下的SS
RDataSeg    ENDS
;----------------------------------------------------------------------------
RCodeSeg    SEGMENT PARA USE16
         ASSUME CS:RCodeSeg
;----------------------------------------------------------------------------
Start      PROC
         ASSUME DS:GDTSeg
         ;-----------------
         mov   ax,GDTSeg
         mov   ds,ax
         ;初始化全局描述符表
         mov   bx,16
         mov   ax,CodeKSeg
         mul   bx
         mov   CodeK.BaseL,ax
         mov   CodeK.BaseM,dl
         mov   CodeK.BaseH,dh
         mov   ax,LDTSeg
         mul   bx
         mov   LDTable.BaseL,ax
         mov   LDTable.BaseM,dl
         mov   LDTable.BaseH,dh
         ;設置GDT偽描述符
         ASSUME DS:RDataSeg
         mov   ax,RDataSeg
         mov   ds,ax
         mov   ax,GDTSeg
         mul   bx
         mov   WORD PTR VGDTR.Base,ax
         mov   WORD PTR VGDTR.Base+2,dx
         ;初始化演示任務LDT
         cld
         call  Init_MLDT
         ;保存實方式堆棧指針
         mov   SSVar,ss
         mov   SPVar,sp
         ;裝載GDTR
         lgdt  QWORD PTR VGDTR
         cli
         ;切換到保護方式
         mov   eax,cr0
         or   al,1
         mov   cr0,eax
         JUMP16 <CodeK_Sel>,<OFFSET Virtual1>
Real:      ;又回到實方式
         mov   ax,RDataSeg
         mov   ds,ax
         lss   sp,DWORD PTR SPVar
         sti
         mov   ax,4c00h
         int   21h
Start      ENDP
;----------------------------------------------------------------------------
Init_MLDT    PROC
         push  ds
         mov   ax,LDTSeg
         mov   ds,ax
         mov   cx,LDNum
         mov   si,OFFSET LDT
InitL:     mov   ax,[si].BaseL
         movzx  eax,ax
         shl   eax,4
         shld  edx,eax,16
         mov   [si].BaseL,ax
         mov   [si].BaseM,dl
         mov   [si].BaseH,dh
         add   si,SIZE Desc
         loop  InitL
         pop   ds
         ret
Init_MLDT    ENDP
;----------------------------------------------------------------------------
RCodeSeg    ENDS
         END   Start

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved