|
關(guān)于實(shí)例三的說(shuō)明
有些步驟的實(shí)現(xiàn)方法已在前面的實(shí)例中做過介紹,下面就任務(wù)內(nèi)無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移和使用局部 描述符LDT等作些說(shuō)明:
(1)實(shí)模式下初始化LDT 演示任務(wù)使用了局部描述符表LDT,本實(shí)例中該LDT在實(shí)模式下初始化(當(dāng)然,也可以在使用LDT前 的保護(hù)模式初始化)。為了簡(jiǎn)便,LDT中各描述符的界限和屬性值在定義時(shí)預(yù)置,利用一個(gè)子程序 設(shè)置各段的段基地址。為方便起見,在定義時(shí)把各段的段值安排在相應(yīng)描述符的段基地址低16位 字段中。由于實(shí)例中各段在實(shí)模式下定位(這是因?yàn)槌绦蚴菑膶?shí)模式下啟動(dòng)執(zhí)行的),所以把段值 乘以16就是對(duì)應(yīng)的段基地址。
(2)裝載LDTR寄存器 在使用LDT之前,還要裝載局部描述符表寄存器LDTR。本實(shí)例中的如下兩條指令用于裝載LDTR:
mov ax,LDT_SEL lldt ax LLDT指令是專門用于裝載LDTR的指令。該指令的操作數(shù)是對(duì)應(yīng)LDT段描述符的選擇子。根據(jù)該選擇子, 處理器從GDT中取出相應(yīng)的LDT段描述符,在進(jìn)行合法性等檢查后,LDT段描述符的基地址和界限等信息 被裝入LDTR的高速緩沖寄存器中。由于要引用GDT,所以不能在實(shí)模式下裝載LDTR。在“操作系統(tǒng)類指 令”一文中將對(duì)LLDT指令作詳細(xì)說(shuō)明。
(3)利用段間轉(zhuǎn)移指令JMP實(shí)現(xiàn)任務(wù)內(nèi)無(wú)特權(quán)級(jí)的轉(zhuǎn)移 在本實(shí)例中進(jìn)入保護(hù)方式后,特權(quán)級(jí)是0。通過如下段間直接轉(zhuǎn)移指令實(shí)現(xiàn)從代碼段K到代碼段L的轉(zhuǎn)移:
JUMP16 CodeL_Sel,Virtual2 其中,選擇子CodeL_Sel是對(duì)應(yīng)代碼段L的描述符的選擇子。該描述符在LDT中,所以選擇子中的描述 符表指示位TI為1。描述符特權(quán)級(jí)是0,表示對(duì)應(yīng)代碼段的特權(quán)級(jí)是0,選擇子中的請(qǐng)求特權(quán)級(jí)RPL也 是0。目標(biāo)代碼段不是一致代碼段,所以在CPL=DPL,RPL<=DPL的情況下,順利進(jìn)行相同特權(quán)級(jí)的轉(zhuǎn) 移:目標(biāo)代碼段的選擇子CodeL_Sel被裝入CS,對(duì)應(yīng)描述符中的信息被裝入高速緩沖寄存器中,偏移 量Virtual2被裝入指令指針寄存器。由于是16位代碼段,所以偏移用16位表示。
類似地,通過如下段間直接轉(zhuǎn)移指令實(shí)現(xiàn)從代碼段L轉(zhuǎn)移到代碼段K:
JUMP16 CodeK_Sel,Virtual3 其中,選擇子CodeK_Sel是對(duì)應(yīng)代碼段K的描述符選擇子。由于描述符在GDT中,所以選擇子中的TI位是0。
(4)利用段間調(diào)用指令CALL實(shí)現(xiàn)任務(wù)內(nèi)無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移 在代碼段L中,通過段間直接調(diào)用指令CALL調(diào)用了在代碼段C中的兩個(gè)子程序,這些調(diào)用都是無(wú)特權(quán)級(jí)變 換的轉(zhuǎn)移。例如,利用如下指令調(diào)用了顯示字符串子程序DispMsg:
CALL16 CodeC_Sel,DispMsg 其中,CodeC_Sel是代碼段C的選擇子,DispMsg表示子程序的入口。描述代碼段C的描述符在LDT中, 描述符特權(quán)級(jí)DPL是0,所以使用的選擇子CodeC_Sel的請(qǐng)求特權(quán)級(jí)RPL是0,描述符表指示位TI為1。 目標(biāo)代碼段C不是一致代碼段,所以在CPL=DPL,RPL<=DPL的情況下,順利進(jìn)行相同特權(quán)級(jí)的轉(zhuǎn)移: 當(dāng)前CS和IP壓入堆棧,目標(biāo)代碼段的選擇子CodeC_Sel被裝入CS,對(duì)應(yīng)描述符中的信息被裝入高速緩 沖寄存器中,16位偏移DispMsg被裝入指令指針I(yè)P。由于是16位段,所以偏移用16位表示,壓入堆棧 的是字而不是雙字。
(5)段間返回指令RET實(shí)現(xiàn)任務(wù)內(nèi)無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移 段間返回指令RET從堆棧的棧頂彈出返回地址(由選擇子和偏移)構(gòu)成。彈出選擇子內(nèi)的RPL=CPL,并 且對(duì)應(yīng)DPL=CPL,RPL<=DPL是當(dāng)然的,所以能順利進(jìn)行相同特權(quán)級(jí)的轉(zhuǎn)移。
3.別名技術(shù) 在上述實(shí)例三中,使用了兩個(gè)描述符來(lái)描述演示任務(wù)的LDT段。段描述符LDTable被安排在GDT中, 它是系統(tǒng)段描述符,把段LDTSeg描述成演示任務(wù)的局部描述符表LDT。描述符ToLDT被安排在LDT中, 它是數(shù)據(jù)段描述符,把段LDTSeg描述成一個(gè)普通數(shù)據(jù)段。描述符LDTable被裝載到LDTR,描述 符ToLDT被裝載到某個(gè)數(shù)據(jù)段寄存器。為什么要這樣處理呢?根據(jù)實(shí)例三的功能要求,需要訪問演示 任務(wù)的局部描述符表LDT段,以取得代碼段L的段界限值,這需要通過某個(gè)段寄存器進(jìn)行, 但不能把系統(tǒng)段描述符的選擇子裝載到段寄存器,所以采 用兩個(gè)描述符來(lái)描述段LDTSeg。
這種為了滿足對(duì)同一個(gè)段實(shí)施不同方式操作的需要,而用多個(gè)描述符加以描述的技術(shù)稱為別名技術(shù)。 在保護(hù)方式程序設(shè)計(jì)中,常常要采用別名技術(shù)。例如:用兩個(gè)具有不同類型值的描述符來(lái)描述同一個(gè) 段。再如,用兩個(gè)具有不同DPL的描述符來(lái)描述同一個(gè)段。
<三>任務(wù)內(nèi)不同特權(quán)級(jí)的變換 在一個(gè)任務(wù)內(nèi)可以存在四種特權(quán)級(jí),所以常常會(huì)發(fā)生不同特權(quán)級(jí)之間的變換。例如,外層的應(yīng)用程 序調(diào)用內(nèi)層操作系統(tǒng)的例程,以獲得必要的諸如存儲(chǔ)器分配等系統(tǒng)服務(wù)。內(nèi)層操作系統(tǒng)的例程完成 后,返回到外層應(yīng)用程序。
在同一任務(wù)內(nèi),實(shí)現(xiàn)特權(quán)級(jí)從外層到內(nèi)層變換的普通途徑是使用段間調(diào)用指令CALL,通過調(diào)用門進(jìn) 行轉(zhuǎn)移;實(shí)現(xiàn)特權(quán)級(jí)從內(nèi)層向外層變換的普通途徑是使用段間返回指令RET。注意,不能用JMP指令 實(shí)現(xiàn)任務(wù)內(nèi)不同特權(quán)級(jí)的變換。
1.通過調(diào)用門的轉(zhuǎn)移 當(dāng)段間轉(zhuǎn)移指令JMP和段間調(diào)用指令CALL所含指針的選擇子指示調(diào)用門描述符時(shí),就可以實(shí)現(xiàn)通過 調(diào)用門的轉(zhuǎn)移。但只有CALL指令能變換到內(nèi)層的特權(quán)級(jí),JMP指令只能轉(zhuǎn)移到同級(jí)的代碼。
調(diào)用門描述符轉(zhuǎn)移的入口點(diǎn)包含目標(biāo)地址的段及偏移量的48位全指針。在執(zhí)行通過任務(wù)門的段間轉(zhuǎn) 移指令JMP或段間調(diào)用指令CALL時(shí),指令所含指針內(nèi)的選擇子用 于確定調(diào)用門,而偏移被丟棄;把調(diào)用門內(nèi)的48位全指針作為目標(biāo)地址指針進(jìn)行轉(zhuǎn)移。
處理器采用與訪問數(shù)據(jù)段相同的特權(quán)級(jí)規(guī)則控制對(duì)門描述符的訪問。調(diào)用門描述符的DPL規(guī)定了訪 問該門的最外層特權(quán)級(jí),在取出調(diào)用門內(nèi)的48位全指針,把它作為目標(biāo)地址指針向目標(biāo)代碼段轉(zhuǎn)移 之前,要進(jìn)行特權(quán)級(jí)檢查。只有在相同級(jí)或者更內(nèi)層特權(quán)級(jí)的程序才可訪問調(diào)用門,即CPL<=調(diào)用 門的DPL。同時(shí),還要求指示門的選擇子的RPL必須滿足RPL<=調(diào) 用門的DPL的條件。檢測(cè)通過后,才開始向目標(biāo)代碼段轉(zhuǎn)移的步驟。其中還要檢測(cè)目標(biāo)描述 符是否為代碼段描述符,調(diào)用門內(nèi)的選擇子指示的描述符必須是代碼段描述符。此外,在裝載代碼 段描述符高速緩沖寄存器之前調(diào)整代碼段選擇子的RPL=0,也即調(diào)用門中代碼段選擇子的RPL被忽略。
在裝載CS高速緩沖寄存器時(shí),還要對(duì)目標(biāo)代碼段描述符進(jìn)行保護(hù)檢測(cè)。檢測(cè)過程中的DPL不再是調(diào) 用門的DPL,而是調(diào)用門內(nèi)選擇子所指示的目標(biāo)代碼段描述符的DPL。段間調(diào)用指令CALL和段間轉(zhuǎn)移 指令JMP所做的檢測(cè)不一樣。
對(duì)于使用調(diào)用門的段間轉(zhuǎn)移指令JMP,檢測(cè)條件與段間直接轉(zhuǎn)移相同。由于已置RPL=0,所以可認(rèn)為 RPL<=DPL的條件總能滿足。所以,對(duì)于普通的非一致代碼段,當(dāng)CPL=DPL時(shí),發(fā)生無(wú)特權(quán)級(jí)變換的 轉(zhuǎn)移;對(duì)于一致代碼段,在滿足CPL>=DPL時(shí)也發(fā)生無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移;其它情形就引起異常。
對(duì)于使用調(diào)用門的段間調(diào)用指令CALL,情形就不同了。由于已置RPL=0,所以可認(rèn)為RPL<=DPL的條 件總能滿足。對(duì)于一致代碼段,在滿足CPL>=DPL時(shí)發(fā)生無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移。對(duì)于非一致代碼段, 當(dāng)CPL=DPL時(shí),仍發(fā)生無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移;當(dāng)CPL>DPL時(shí),就發(fā)生向內(nèi)層特權(quán)級(jí)變換的轉(zhuǎn)移,將調(diào) 用門中的選擇子和偏移裝入CS和指令指針EIP中,并使CPL保持等于DPL,同時(shí)切換到內(nèi)層堆棧。
綜上所述,使用段間調(diào)用指令CALL,通過調(diào)用門可以實(shí)現(xiàn)從外層程序調(diào)用進(jìn)入內(nèi)層程序(JMP指令只 能實(shí)現(xiàn)無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移);通過調(diào)用門也可實(shí)現(xiàn)無(wú)特權(quán)級(jí)變換的轉(zhuǎn)移。需要注意的是,JMP指令 和CALL指令都不能實(shí)現(xiàn)向外層特權(quán)級(jí)的轉(zhuǎn)移否則會(huì)引起異常。
當(dāng)然,CALL指令在最后把目標(biāo)代碼段的指針裝入CS和EIP之前,要把原CS和EIP,即返回地址保存到 堆棧。如無(wú)特權(quán)級(jí)變換,堆棧保持不變,返回地址就保存在原堆棧中;否則,返回地址保存在內(nèi)層 堆棧中。
2.堆棧切換 在使用CALL指令通過調(diào)用門向內(nèi)層轉(zhuǎn)移時(shí),不僅特權(quán)級(jí)發(fā)生變換,控制轉(zhuǎn)移到一個(gè)新的代碼段,而 且也切換到內(nèi)層的堆棧段。從本教程第五篇的任務(wù)狀態(tài)段TSS的格式可見,TSS中包含有指 向0級(jí)、1級(jí)和2級(jí)堆棧的指針。在特權(quán)級(jí)發(fā)生向內(nèi)層變換時(shí),根據(jù)變換到的特權(quán)級(jí)使用TSS中相應(yīng)的 堆棧指針對(duì)SS及ESP寄存器進(jìn)行初始化,建立起一個(gè)空棧。
在建立起內(nèi)層堆棧后,處理器先把外層堆棧的指針SS及ESP寄存器的值壓入內(nèi)層堆棧,以使得相應(yīng) 的向外層返回可恢復(fù)原來(lái)的外層堆棧。然后,從外層堆棧復(fù)制以雙字為單位的調(diào)用參數(shù)到內(nèi)層堆棧 中,調(diào)用門中的DCOUNT字段值決定了復(fù)制參數(shù)的數(shù)量。這些被復(fù)制的參數(shù)是主程序通過堆棧傳遞給 子程序的實(shí)參,在調(diào)用之前被壓入外層堆棧。通過復(fù)制棧中的參數(shù),使內(nèi)層的子程序不需要考慮堆 棧的切換,而容易地訪問主程序傳遞過來(lái)的實(shí)參。最后,調(diào)用的返回地址被壓入堆棧,以便在調(diào)用 結(jié)束時(shí)返回。下圖為在向內(nèi)層變換時(shí),建立內(nèi)層堆棧,并從外層堆棧復(fù)制2個(gè)雙字參數(shù)到內(nèi)層堆棧 的示意圖。圖中每項(xiàng)是雙字,可見的段寄存器內(nèi)的選擇子被擴(kuò)展成32為存入堆棧,高16位為0。對(duì) 于16位的使用調(diào)用門的段也是如此。
需要注意的是,無(wú)論是否通過調(diào)用門,只要不發(fā)生特權(quán)級(jí)變換,就不會(huì)切換堆棧。
3.向外層返回 與使用CALL指令通過調(diào)用門向內(nèi)層變換相反,使用RET指令實(shí)現(xiàn)向外層返回。段間返回指令RET從 堆棧中彈出返回地址,并且可以采用調(diào)整ESP的方法,跳過相應(yīng)的在調(diào)用之前壓入堆棧的參數(shù)。返 回地址的選擇子指示要返回的代碼段的描述符,從而確定返回的代碼段。選擇子的RPL確定返回后 的特權(quán)級(jí),而不是對(duì)應(yīng)描述符的DPL,這是因?yàn)椋伍g返回指令RET可能使控制返回到一致代碼段, 而一致代碼段可以在DPL規(guī)定的特權(quán)級(jí)以外的特權(quán)級(jí)執(zhí)行。需要注意的是,RET指令所使用的返回 地址的選擇子只能使用代碼段描述符,而不能使用任何系統(tǒng)段描述符或門描述符,當(dāng)然,更不能 使用數(shù)據(jù)段描述符,否則會(huì)引起異常。與CALL指令相對(duì)應(yīng),RET指令也不能向內(nèi)層返回。
段間返回指令完成返回的步驟如下:
(1)RET指令先從堆棧中彈出返回地址。如果彈出地址的選擇子的RPL規(guī)定相對(duì)于CPL更外層的特權(quán) 級(jí),那么就引起向外層返回。
(2)為向外層返回,跳過內(nèi)層堆棧中的參數(shù),再?gòu)膬?nèi)層棧中彈出指向外層堆棧的指針,并裝入SS及 ESP,以恢復(fù)外層堆棧。
(3)調(diào)整ESP,跳過在相應(yīng)的調(diào)用之前壓入到外層堆棧的參數(shù)。即返回指令不但彈出內(nèi)層棧的參數(shù), 而且也彈出外層棧的參數(shù)。
(4)然后,檢查數(shù)據(jù)段寄存器DS、ES、FS及GS,以保證尋址的段在外層是可訪問的,如果段寄存器 尋址的段在外層是不可訪問的,那么裝入一個(gè)空選擇子,以避免在返回時(shí)發(fā)生保護(hù)空洞。
(5)返回外層繼續(xù)執(zhí)行。
上述五步是對(duì)帶立即數(shù)的段間返回指令而言的,立即數(shù)規(guī)定了堆棧中要跳過的參數(shù)的字節(jié)數(shù)。對(duì)于 無(wú)立即數(shù)的段間返回指令缺少第二步和第三步。若RET指令不需要向外層返回,那么就只 有(1)和(5)兩步。對(duì)于有通過堆棧傳遞參數(shù)的子程序,必須使用帶立即數(shù)的返回指令返回,否則返 回時(shí)會(huì)裝載錯(cuò)誤的外層棧指針。
若不使用帶立即數(shù)的返回指令,可以在返回前把外層棧的棧指針存入內(nèi)層棧中的用于保存返回地址 上方兩個(gè)雙字的區(qū)域中,由外層返回的過程可知,這可正確恢復(fù)外層棧的指針,但在外層程序中, 必須人為調(diào)整外層棧指針,以便廢除在外層棧中壓入的參數(shù)。在使用C調(diào)用約定的程序中可使用此 方法。但這會(huì)增加代碼的長(zhǎng)度和處理時(shí)間,使代碼效率變低。正因?yàn)槿绱耍赪indows 9X下,新增 加了一種STDCALL的調(diào)用約定,它正是為了適應(yīng)Intel系列處理器的體系結(jié)構(gòu)而產(chǎn)生的。
<四>演示任務(wù)內(nèi)特權(quán)級(jí)變換的實(shí)例(實(shí)例四) 下面給出一個(gè)演示任務(wù)內(nèi)特權(quán)級(jí)變換的實(shí)例。該實(shí)例演示在任務(wù)內(nèi)通過調(diào)用門從外層特權(quán)級(jí)變換到 內(nèi)層特權(quán)級(jí);也演示通過段間返回指令從內(nèi)層特權(quán)級(jí)變換到外層特權(quán)級(jí);還演示通過調(diào)用門的無(wú)特 權(quán)級(jí)變換的轉(zhuǎn)移。實(shí)例使用了任務(wù)狀態(tài)段TSS,這是因?yàn)槿蝿?wù)內(nèi)特權(quán)級(jí)變換時(shí)要使用的內(nèi)層堆棧指 針存放在TSS中。
1.實(shí)現(xiàn)步驟 該實(shí)例的實(shí)現(xiàn)步驟為:
(1)實(shí)方式下初始化;
(2)切換到保護(hù)模式;
(3)設(shè)置TR和LDTR。由于在任務(wù)內(nèi)發(fā)生特權(quán)級(jí)變換時(shí)要切換堆棧,而內(nèi)層堆棧的指針存放在當(dāng)前任 務(wù)的TSS中,所以在進(jìn)入保護(hù)模式后設(shè)置任務(wù)狀態(tài)段寄存器TR。由于演示任務(wù)使用了局部描述符表 ,所以設(shè)置LDTR;
(4)經(jīng)調(diào)用門進(jìn)入32位過渡代碼段;
(5)建立返回3級(jí)演示代碼段的環(huán)境;
(6)利用RET指令轉(zhuǎn)移到3級(jí)的演示代碼段。為了演示外層程序通過調(diào)用門調(diào)用內(nèi)層程序,要 使CPL>0。實(shí)例先通過段間返回指令RET從特權(quán)級(jí)0變換到特權(quán)級(jí)3的演示代碼段。在特權(quán)級(jí)3下, 通過調(diào)用門調(diào)用1級(jí)的子程序。隨著執(zhí)行段間返回指令RET,又回到3級(jí)的演示代碼段;
(7)在3級(jí)的演示代碼段中,經(jīng)調(diào)用門轉(zhuǎn)移到0級(jí)的32位過渡代碼段;
(8)直接轉(zhuǎn)0級(jí)的臨時(shí)代碼段;
(9)準(zhǔn)備返回實(shí)模式;
(10)切換回實(shí)模式;
(11)實(shí)模式下的恢復(fù)工作。
2.源程序組織和清單 實(shí)例四由如下部分組成:
(1)全局描述符表GDT。GDT含有演示任務(wù)的TSS段描述符和LDT段描述符,此外還含有臨時(shí)代碼段 的描述符、規(guī)范數(shù)據(jù)段描述符和視頻緩沖區(qū)段描述符。
(2)演示任務(wù)的LDT段。它含有除臨時(shí)代碼段外的其它代碼段的描述符和演示任務(wù)各級(jí)堆棧段描 述符,還含有3個(gè)調(diào)用門。
(3)演示任務(wù)的TSS段。
(4)演示任務(wù)的0級(jí)、1級(jí)和3級(jí)堆棧段。
(5)顯示子程序段。32位代碼段,特權(quán)級(jí)1。
(6)演示代碼段。32位代碼段,特權(quán)級(jí)3。
(7)過渡代碼段。32位段,特權(quán)級(jí)0。
(8)臨時(shí)代碼段。16位段,特權(quán)級(jí)0。
(9)實(shí)模式下的數(shù)據(jù)和代碼段。
該實(shí)例的邏輯功能是顯示演示代碼段執(zhí)行時(shí)的當(dāng)前特權(quán)級(jí)CPL。源程序清單如下:
|
|
|
來(lái)自: skywood > 《Daily Study》