在讀代碼的時候,Jump指令用的特別多, 在中斷/異常, 虛實地址轉換, 任務切換等等等等,都用到了Jump指令,今天我們來討論一下究竟Jump都做了什麼事情.
下面用僞指令來描述Jump做了什麼事情, 又是怎麼區分這些事件的
JMP(SelectorType Selector, int Offset)
{
SegAttributes Attributes;
SelectorType GSelector;
int Base, Limit, GOffset;
if((Selector & 0FFFCh) == 0)
SegmentException($GP, 0);
ReadDescriptor(Selector,&Attributes,&Base,&Limit,&GSelector,&GOffset);
if(Attributes.DType){
CSDescriptorLoad(Selector, Attributes,Base,Limit, $GP);
if(Offset > CS.Limit)
SegmentException($GP,0);
CS.Selector= Selector;
CS.Selector.RPL = CPL;
EIP = Offset;
}
else{
if((Attributes.DPL < CPL) || (Attributes.DPL < Selector.RPL))
SegmentException($GP, Selector);
switch(Attributes.Type){
case 1:
if(Attributes.P == 0)
SegmentException($NP, Selector);
TaskSwitch286(Selector, Attributes, Base, Limit, 0);
break;
case 5:
if(Attributes.P == 0)
SegmentException($NP, Selector);
TaskGate(GSelector, 0);
break;
case 9:
if(Attributes.P == 0)
SegmentException($NP, Selector);
TaskSwitch(Selector, Attributes, Base, Limit, 0);
break;
case 4:
if(Attributes.P == 0)
SegmentException($NP, Selector);
JumpGate286(GSelector, GOffset, $GP);
break;
case 12:
if(Attributes.P == 0)
SegmentException($NP, Selector);
JumpGate386(GSelector, GOffset, $GP);
break;
Default:
SegmentException($GP, Selector);
}
}
}
1. 裝入的參數爲Selector 和 Offset
3. 定義變量Attribute, 對應段描述符中的屬性字段
4. 定義變量Gselector, 如果是類型爲門, 則此描述符存放的是選擇子Selector和偏移Offset, 再根據Gselector找到對應的段描述符.
5. 定義基地址, 界限和Goffset(偏移, 用於系統段或門)
6-7 選擇子爲空, 產生段異常.
8. 根據選擇子, 返回段描述符中的屬性, 基地址和段界限. 如果爲門, 則返回門選擇子和偏移.
9. 如果描述符爲存儲段
10. CSDescriptorLoad函數會進行特權級檢查,如果出錯,則會出現段異常. 再把Attribute,Base, Limit裝入到CS 投影寄存器.(經常說的清CPU的prefetch queue)
11-12檢查是否越界, 是則產生段異常
13. 單獨裝入Selector.(MOV指令無法修改CS寄存器,只能靠JUMP, CALL和IRET指令)
14. RPL裝入CPL, 可以仔細思考一下爲什麼這麼做?
15. EIP裝入Offset. 和上面的CS組合起來, 就是CS:EIP, 這就實現了跳轉了.
17. 如果描述符類型爲系統段或門
18-19特權級檢查, 出錯則調用段異常函數
20. 根據屬性中的類型, 進行不同的處理
21-25 調用TaskSwitch286進行任務切換, 參數0表示無鏈接
26-30 調用任務門TaskGate() 函數, 0 表示 無鏈接
31-35 調用任務切換TaskSwitch() 函數
36-40 調用286 調用門JumpGate286()
41-45調用386調用門JumpGate386()