通过调用门进行控制转移 ——《x86汇编语言:从实模式到保护模式》读书笔记29
通過調用門進行控制轉移
1. 關于堆棧切換
如果通過調用門把控制轉移到了更高特權級的非一致代碼段中,那么CPL就會被設置為目標代碼段的DPL值,并且會引起堆棧切換。為什么要切換堆棧呢?原因有以下幾點: 
 1. 因為棧段的特權級必須同CPL保持一致; 
 2. 防止高特權級程序由于棧空間不足而崩潰; 
 3. 防止低特權級程序通過共享的棧有意或無意地干擾高特權級程序。
為了切換棧,每個任務除了自己的固有棧之外,還必須額外定義一套或多套棧,具體是多少取決于任務的特權級別。 
 0特權級的任務不需要額外的棧,因為除了從調用高特權級的例程(通常是操作系統例程)返回外,不允許將控制從特權級高的代碼段轉移到特權級低的代碼段——操作系統不會引用可靠性比自己低的代碼;1特權級的任務需要額外定義一個DPL為0的棧,以便將控制轉移到0特權級時使用;2特權級的任務需要額外定義兩個棧,其DPL分別為0和1;3特權級的任務最多額外定義三個棧,其DPL分別為0、1、2.
以下文字摘自《Intel Architecture Software Developer’s Manual Volume 3:System Programming》的4.8.5節——Stack Switching.
Each task must define up to 4 stacks: one for applications code (running at privilege level 3) and one for each of the privilege levels 2, 1, and 0 that are used. (If only two privilege levels are used[3 and 0], then only two stacks must be defined.)
操作系統負責為任務用到的所有特權級分配棧空間和創建棧段描述符,并且在任務的TSS中填寫棧段的選擇子和ESP的初始值(下圖是TSS的一部分)。
有幾點需要注意: 
 1. 每個棧必須可讀可寫,并且具有足夠的空間來存放以下信息: 
 (1)調用過程的SS、ESP、CS和EIP寄存器的內容 
 (2)被調用過程的參數和臨時變量所需使用的空間 
 (3)當隱含調用一個異常或者中斷過程時標志寄存器EFLAGS和出錯碼使用的空間 
 2. 由于一個過程可以調用其他過程,因此每個棧必須有足夠的空間來容納多幀信息 
 3. TSS中的中的SSx、ESPx(x=0,1,2)字段是靜態的,除非軟件進行修改,處理器從來不會改變它們。舉例來說,假設操作系統為一個用戶任務的TSS填寫了ESP0,其值為0x800;當這個任務通過調用門進入0特權級的代碼段時,會切換到0特權級堆棧,堆棧指針ESP的初始值就是0x800;返回時,假設ESP變成了0x808,處理器并不會把0x808更新到TSS中的ESP0域;下次再通過調用門進入0特權級代碼段時,使用的還是當初設置的靜態值0x800。
2. 通過調用門進行控制轉移和返回的具體過程
2.1 轉移的過程
首先,通過調用門進行控制轉移,可以使用jmp far或者call far指令。指令執行時,段選擇子必須指向調用門,32偏移量可以是任意值(會被CPU忽略)。
其次,必須符合下表的特權級檢查規則。
再次,當使用call far指令通過調用門轉移控制時,如果改變了CPL,則必須切換棧,即從當前任務的固有棧切換到與目標代碼段特權級相同的棧上。棧的切換是由處理器固件自動進行的。
當前棧是由SS和ESP的當前內容所指示的。要切換到的新棧的相關信息位于當前任務的TSS中,處理器知道如何找到它。棧切換過程如下: 
 1. 根據目標代碼段的DPL(也就是新的CPL)到當前任務的TSS中讀取新棧的選擇子和棧指針。在讀取棧選擇子、棧指針或者棧段描述符的過程中,任何違反段界限的錯誤都將導致產生一個無效TSS異常。 
 2. 檢查棧段描述符的特權級和類型是否有效,若無效同樣產生一個無效TSS異常。 
 3. 臨時保存SS和ESP的當前值,把新棧的選擇子和棧指針加載到SS和ESP中。然后把臨時保存的SS和ESP的內容壓入新棧中。 
 4. 根據調用門描述符中“參數個數”字段,把舊棧中的所有參數復制到新棧中。如果參數個數為0,則不復制參數。 
 5. 將當前CS和EIP的內容壓入新棧。通過調用門實施的控制轉移一定是遠轉移,所以要壓入CS和EIP。 
 6. 從調用門描述符中把目標代碼段的選擇子和段內偏移值傳送到CS和EIP中,開始執行被調用過程。
相反,如果沒有改變特權級別,則不切換棧,繼續使用調用者當前的棧,只是在原來的基礎上壓入當前CS和EIP的內容。
另外,如果通過調用門的控制轉移是jmp far指令發起的,結果就是“肉包子打狗——有去無回”,且沒有特權級變化,也不需要切換棧。相反,如果是call far指令發起的,則可以使用遠返回指令retf把控制返回到調用者。
2.2. 返回的過程
對于相同特權級的返回,CPU從堆棧中彈出EIP和CS;會發生特權級改變的遠返回僅允許返回到低特權級程序中,即返回到的代碼段的DPL在數值上要大于CPL。返回的全部過程如下:
總結
以上是生活随笔為你收集整理的通过调用门进行控制转移 ——《x86汇编语言:从实模式到保护模式》读书笔记29的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 编写一个可以打印不同长度数组内容的函数模
- 下一篇: linux怎么不更新内核,Linux升级
