敬业的IT人 >> 编程开发 >> 其他语言 >> 控制转移(1)

控制转移(1)

敬业的IT人 互联网 佚名 2008-2-19 15:33:54
    控制转移基本上可分为两大类:同一任务内的控制转移和任务间的控制转移(任务切换)。同一任务内的控制转移又可分为:段内转移、特权级不变的段间转移和特权级变换的段间转移。段内转移与实模式下相似,不涉及特权级变换和任务切换。只有段间转移才涉及特权级变换和任务切换。本文介绍保护方式下的控制转移,重点是任务内的特权级变换和任务间的切换。

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

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

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 进入讨论组讨论。
粤ICP备06119539号
Copyright CiscoSky.Org,Some Rights Reserved.
Email:me1228#tom.com