AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > 汇编语言

控制转移(1)

51自学网 http://www.51zixue.net
    控制转移基本上可分为两大类:同一任务内的控制转移和任务间的控制转移(任务切换)。同一任务内的控制转移又可分为:段内转移、特权级不变的段间转移和特权级变换的段间转移。段内转移与实模式下相似,不涉及特权级变换和任务切换。只有段间转移才涉及特权级变换和任务切换。本文介绍保护方式下的控制转移,重点是任务内的特权级变换和任务间的切换。

<一>任务内无特权级变换的转移

    各种段内转移与实模式下相似,当然不涉及特权级变换和任务切换。只有各种形式的段间转移才涉及特权级变换和任务切换。

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=',0Buffer          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,01001110bDisp1:          mov     al,BYTE PTR fs:[si]                inc     si                or      al,al                jz      Disp2                mov     WORD PTR es:[di],ax                inc     di                inc     di                jmp     Disp1Disp2:          retDispMsg         ENDP;----------------------------------------------------------------------------;把AL寄存器低4位二进制数(一位16进制数)转换成ASCII码;----------------------------------------------------------------------------HToASCII        PROC    FAR                and     al,00001111b                add     al,90h                daa                adc     al,40h                daa                retHToASCII        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,Virtual3CodeLLen        =       $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,Virtual2Virtual3:       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      ?                         ;用于保存实方式下的SPSSVar           DW      ?                         ;用于保存实方式下的SSRDataSeg        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     21hStart           ENDP;----------------------------------------------------------------------------Init_MLDT       PROC                push    ds                mov     ax,LDTSeg                mov     ds,ax                mov     cx,LDNum                mov     si,OFFSET LDTInitL:          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                retInit_MLDT       ENDP;----------------------------------------------------------------------------RCodeSeg        ENDS                END     Start

 

 

 
上一篇:分页管理机制  下一篇:控制转移(2)