控制转移基本上可分为两大类:同一任务内的控制转移和任务间的控制转移(任务切换)。同一任务内的控制转移又可分为:段内转移、特权级不变的段间转移和特权级变换的段间转移。段内转移与实模式下相似,不涉及特权级变换和任务切换。只有段间转移才涉及特权级变换和任务切换。本文介绍保护方式下的控制转移,重点是任务内的特权级变换和任务间的切换。 <一>任务内无特权级变换的转移 各种段内转移与实模式下相似,当然不涉及特权级变换和任务切换。只有各种形式的段间转移才涉及特权级变换和任务切换。 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的段界限的值。源程序如下: INCLUDE 386SCD.INCGDTSeg SEGMENT PARA USE16 'GDT' GDT LABEL BYTE DUMMY Desc <> Normal Desc <0ffffh,,,ATDW,,> CodeK Desc <0ffffh,,,ATCE,,> LDTable Desc <LDTLen-1,,,ATLDT,,> GDTLen = $-GDT Normal_Sel = Normal-GDT CodeK_Sel = CodeK-GDT LDT_Sel = LDTable-GDT GDTSeg ENDS LDTSeg SEGMENT PARA USE16 'LDT' LDT LABEL BYTE CodeL Desc <CodeLLen-1,CodeLSeg,,ATCE,,>CodeC Desc <CodeCLen-1,CodeCSeg,,ATCE,,>VideoBuf Desc <0ffffh,0b800h,,ATDW,,>ToLDT Desc <LDTLen-1,LDTSEG,,ATDR+DPL3,,>MData Desc <MDataLen-1,MDataSeg,,ATDW+DPL3,,>StackS Desc <TopOfS-1,StackSeg,,ATDWA,,>LDTLen = $-LDT LDNum = ($-LDT)/(SIZE Desc) CodeL_Sel = CodeL-LDT+TIL CodeC_Sel = CodeC-LDT+TIL Video_Sel = VideoBuf-LDT+TIL ToLDT_Sel = ToLDT-LDT+TIL 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' ASSUME CS:CodeCSegDispMsg 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 ENDPHToASCII PROC FAR and al,00001111b add al,90h daa adc al,40h daa retHToASCII ENDPCodeCLen = $CodeCSeg ENDS CodeLSeg SEGMENT PARA USE16 'CODEL' ASSUME CS:CodeLSegVirtual2 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 mov gs,ax mov dx,WORD PTR gs:CodeL.LimitL mov si,OFFSET Buffer 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 ENDPCodeLSeg ENDSCodeKSeg SEGMENT PARA USE16 'CODEK' ASSUME CS:CodeKSegVirtual1 PROC FAR mov ax,LDT_Sel LLDT ax 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 ENDPCodeKSeg ENDSRDataSeg SEGMENT PARA USE16 VGDTR PDesc <GDTLen-1,> SPVar DW ? SSVar DW ? RDataSeg ENDSRCodeSeg SEGMENT PARA USE16 ASSUME CS:RCodeSegStart 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 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 cld call Init_MLDT mov SSVar,ss mov SPVar,sp 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 ENDPInit_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 ENDPRCodeSeg ENDS END Start  
|