操作碼 |
指令 |
說明 |
EB cb |
JMP rel8 |
相對短跳轉,位移量相對於下一條指令 |
E9 cw |
JMP rel16 |
相對近跳轉,位移量相對於下一條指令 |
E9 cd |
JMP rel32 |
相對近跳轉,位移量相對於下一條指令 |
FF /4 |
JMP r/m16 |
絕對間接近跳轉,地址由 r/m16 給出 |
FF /4 |
JMP r/m32 |
絕對間接近跳轉,地址由 r/m32 給出 |
EA cd |
JMP ptr16:16 |
絕對遠跳轉,地址由運算元給出 |
EA cp |
JMP ptr16:32 |
絕對遠跳轉,地址由運算元給出 |
FF /5 |
JMP m16:16 |
絕對間接遠跳轉,地址由 m16:16 給出 |
FF /5 |
JMP m16:32 |
絕對間接遠跳轉,地址由 m16:32 給出 |
將程序控制權轉移到指令流中的另一個點,不記錄返回資訊。目標(跳轉目標)運算元指定要跳轉到的指令地址。此運算元可以是立即數、通用暫存器或記憶體位置。
此指令可用於執行四種不同型別的跳轉:
近跳轉 - 跳轉到目前程式碼段(CS 暫存器目前指向的段)內的指令,有時稱為段內跳轉。
短跳轉 - 跳轉範圍限制為距目前 EIP 值 -128 到 +127 的近跳轉。
遠跳轉 - 跳轉到目前程式碼段以外的段(但特權級別相同)中的指令,有時稱為段間跳轉。
任務切換 - 跳轉到另一個任務中的指令。
任務切換只能在保護模式中執行(如需有關使用 JMP 指令執行任務切換的詳細資訊,請參閱“IA-32 英特爾(R) 體系結構軟件開發人員手冊”第 3 卷第 6 章“任務管理”)。
近跳轉與短跳轉。執行近跳轉時,處理器跳轉到目標運算元指定的地址(在目前程式碼段內)。目標運算元指定絕對偏移量(相對於程式碼段基址的偏移量)或相對偏移量(相對於 EIP 暫存器中指令指針目前值的有符號位移量)。跳轉到 8 位相對偏移量 (rel8) 的近跳轉稱為短跳轉。執行近跳轉或短跳轉時,CS 暫存器保持不變。
絕對偏移量在通用暫存器或記憶體位置(r/m16 或 r/m32)中間接指定。運算元大小屬性確定目標運算元的大小(16 位或 32 位)。絕對偏移量直接載入到 EIP 暫存器。如果運算元大小屬性是 16,則 EIP 暫存器的兩個高位位元組清除為零,得到大小最大為 16 位的指令指針。
在彙編程式碼中,相對偏移量(rel8、rel16 或 rel32)通常指定為標籤,但是在機器程式碼級別,它的編碼形式是有符號的 8 位、16 位或 32 位立即數。此值會加到 EIP 暫存器中的值上。(這裡,EIP 暫存器包含 JMP 指令的后一條指令的地址)。使用相對偏移量時,操作碼(對於短跳轉與近跳轉)與運算元大小屬性(對於相對近跳轉)確定目標運算元的大小(8 位、16 位或 32 位)。
“實地址”或“虛 8086”模式中的遠跳轉。在實地址或虛 8086 模式中執行遠跳轉時,處理器跳轉到目標運算元指定的程式碼段與偏移量。這裡,絕對遠地址由目標運算元使用指針(ptr16:16 或 ptr16:32)直接指定,或是使用記憶體位置(m16:16 或 m16:32)間接指定。使用指針方法時,被呼叫過程的段與地址在指令中分別使用 4 位元組(16 位運算元大小)或 6 位元組(32 位運算元大小)遠地址立即數進行編碼。使用間接方法時,目標運算元指定記憶體位置,它包含 4 位元組(16 位運算元大小)或 6 位元組(32 位運算元大小)遠地址。遠地址直接載入到 CS 與 EIP 暫存器。如果運算元大小屬性為 16,則 EIP 暫存器的兩個高位位元組清除為零。
保護模式中的遠跳轉。處理器在保護模式中執行時,JMP 指令可用於執行以下三種類型的遠跳轉:
遠跳轉到相容或非相容程式碼段。
通過呼叫門進行的遠跳轉。
任務切換。
(JMP 指令無法用於執行特權級別間遠跳轉)。
在保護模式中,處理器總是使用遠地址中的段選擇器部分訪問 GDT 或 LDT 中相應的描述符。描述符型別(程式碼段、呼叫門、任務門或 TSS)與訪問許可權確定要執行的跳轉型別。
如果選擇的是程式碼段描述符,則遠跳轉到特權級別相同的程式碼段。(如果選擇的程式碼段在另一個特權級別中,並且程式碼段為非相容程式碼段,則產生一般保護性異常)。在保護模式中遠跳轉到相同的特權級別,與在實地址或虛 8086 模式中執行的遠跳轉非常相似。絕對遠地址由目標運算元使用指針(ptr16:16 或 ptr16:32)直接指定,或是使用記憶體位置(m16:16 或 m16:32)間接指定。運算元大小屬性確定遠地址中偏移量的大小(16 位或 32 位)。新的程式碼段選擇器及其描述符載入到 CS 暫存器,相對於指令的偏移量載入到 EIP 暫存器。請注意,呼叫門(在下一段敘述)也可用於執行相同特權級別上程式碼段的遠呼叫。此機制提供另一層面的間接跳轉,進行 16 位與 32 位程式碼段之間的跳轉時,應首選此方法。
通過呼叫門執行遠跳轉時,由目標運算元指定的段選擇器確定呼叫門。(忽略目標運算元的偏移量部分)。然後處理器跳轉到呼叫門描述符指定的程式碼段,並開始執行呼叫門中指定的偏移量上的指令。此時不發生堆疊切換。同樣地,在這裡,目標運算元可以使用指針(ptr16:16 或 ptr16:32)直接指定呼叫門的絕對遠地址,或是使用記憶體位置(m16:16 或 m16:32)間接進行指定。
使用 JMP 指令執行任務切換與通過呼叫門執行跳轉存在一定程度的相似。這裡,目標運算元指定要切換到的任務的任務門段選擇器(忽略目標運算元的偏移量部分)。任務門則指向任務的 TSS,它包含任務程式碼與堆疊段的段選擇器。TSS 還包含掛起任務之前要執行的下一條指令的 EIP 值。此指令指針值載入到 EIP 暫存器,以便任務從這個下一條指令再次執行。
JMP 指令也可直接指定 TSS 的段選擇器,這樣就不用間接通過任務門執行。如需有關任務切換機制的詳細資訊,請參閱“IA-32 英特爾(R) 體系結構軟件開發人員手冊”第 3 卷第 6 章“任務管理”。
請注意,使用 JMP 指令執行任務切換時,不會設定 EFLAGS 暫存器中的巢狀任務標誌 (NT),並且在載入新 TSS 的前一個任務鏈接欄位時,不使用舊任務的 TSS 選擇器。這樣,執行 IRET 指令時就不會返回到前一個任務。使用 JMP 指令切換任務與 CALL 指令在這一點上是不同的,CALL 指令會設定 NT 標誌,並儲存前一個任務鏈接資訊,允許使用 IRET 指令返回呼叫任務。
IF near jump
THEN IF near relative jump
THEN
tempEIP EIP + DEST; (* EIP is instruction following JMP instruction*)
ELSE (* near absolute jump *)
tempEIP DEST;
FI;
IF tempEIP is beyond code segment limit THEN #GP(0); FI;
IF OperandSize 32
THEN
EIP tempEIP;
ELSE (* OperandSize=16 *)
EIP tempEIP AND 0000FFFFH;
FI;
FI:
IF far jump AND (PE 0 OR (PE 1 AND VM 1)) (* real-address or virtual-8086 mode *)
THEN
tempEIP DEST[offset); (* DEST is ptr16:32 or [m16:32] *)
IF tempEIP is beyond code segment limit THEN #GP(0); FI;
CS DEST[segment selector); (* DEST is ptr16:32 or [m16:32] *)
IF OperandSize 32
THEN
EIP tempEIP; (* DEST is ptr16:32 or [m16:32] *)
ELSE (* OperandSize 16 *)
EIP tempEIP AND 0000FFFFH; (* clear upper 16 bits *)
FI;
FI;
IF far jump AND (PE 1 AND VM 0) (* Protected mode, not virtual-8086 mode *)
THEN
IF effective address in the CS, DS, ES, FS, GS, or SS segment is illegal
OR segment selector in target operand null
THEN #GP(0);
FI;
IF segment selector index not within descriptor table limits
THEN #GP(new selector);
FI;
Read type and access rights of segment descriptor;
IF segment type is not a conforming or nonconforming code segment, call gate,
task gate, or TSS THEN #GP(segment selector); FI;
Depending on type and access rights
GO TO CONFORMING-CODE-SEGMENT;
GO TO NONCONFORMING-CODE-SEGMENT;
GO TO CALL-GATE;
GO TO TASK-GATE;
GO TO TASK-STATE-SEGMENT;
ELSE
#GP(segment selector);
FI;
CONFORMING-CODE-SEGMENT:
IF DPL > CPL THEN #GP(segment selector); FI;
IF segment not present THEN #NP(segment selector); FI;
tempEIP DEST[offset);
IF OperandSize=16
THEN tempEIP tempEIP AND 0000FFFFH;
FI;
IF tempEIP not in code segment limit THEN #GP(0); FI;
CS DEST[SegmentSelector); (* segment descriptor information also loaded *)
CS(RPL) CPL
EIP tempEIP;
END;
NONCONFORMING-CODE-SEGMENT:
IF (RPL > CPL) OR (DPL CPL) THEN #GP(code segment selector); FI;
IF segment not present THEN #NP(segment selector); FI;
IF instruction pointer outside code segment limit THEN #GP(0); FI;
tempEIP DEST[offset);
IF OperandSize=16
THEN tempEIP tempEIP AND 0000FFFFH;
FI;
IF tempEIP not in code segment limit THEN #GP(0); FI;
CS DEST[SegmentSelector); (* segment descriptor information also loaded *)
CS(RPL) CPL
EIP tempEIP;
END;
CALL-GATE:
IF call gate DPL < CPL
OR call gate DPL < call gate segment-selector RPL
THEN #GP(call gate selector); FI;
IF call gate not present THEN #NP(call gate selector); FI;
IF call gate code-segment selector is null THEN #GP(0); FI;
IF call gate code-segment selector index is outside descriptor table limits
THEN #GP(code segment selector); FI;
Read code segment descriptor;
IF code-segment segment descriptor does not indicate a code segment
OR code-segment segment descriptor is conforming and DPL > CPL
OR code-segment segment descriptor is non-conforming and DPL CPL
THEN #GP(code segment selector); FI;
IF code segment is not present THEN #NP(code-segment selector); FI;
IF instruction pointer is not within code-segment limit THEN #GP(0); FI;
tempEIP DEST[offset);
IF GateSize=16
THEN tempEIP tempEIP AND 0000FFFFH;
FI;
IF tempEIP not in code segment limit THEN #GP(0); FI;
CS DEST[SegmentSelector); (* segment descriptor information also loaded *)
CS(RPL) CPL
EIP tempEIP;
END;
TASK-GATE:
IF task gate DPL < CPL
OR task gate DPL < task gate segment-selector RPL
THEN #GP(task gate selector); FI;
IF task gate not present THEN #NP(gate selector); FI;
Read the TSS segment selector in the task-gate descriptor;
IF TSS segment selector local/global bit is set to local
OR index not within GDT limits
OR TSS descriptor specifies that the TSS is busy
THEN #GP(TSS selector); FI;
IF TSS not present THEN #NP(TSS selector); FI;
SWITCH-TASKS to TSS;
IF EIP not within code segment limit THEN #GP(0); FI;
END;
TASK-STATE-SEGMENT:
IF TSS DPL < CPL
OR TSS DPL < TSS segment-selector RPL
OR TSS descriptor indicates TSS not available
THEN #GP(TSS selector); FI;
IF TSS is not present THEN #NP(TSS selector); FI;
SWITCH-TASKS to TSS
IF EIP not within code segment limit THEN #GP(0); FI;
END;
發生任務切換時影響所有的標誌;未發生任務切換時則不影響任何標誌。
#GP(0) - 如果目標運算元、呼叫門或 TSS 的偏移量超出程式碼段限制。如果目標運算元、呼叫門、任務門或 TSS 的段選擇器為空。如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。如果 DS、ES、FS、或 GS 暫存器用於訪問記憶體,並且它包含空的段選擇器。
#GP(選擇器) - 如果段選擇器索引超出描述符表格限制。如果目標運算元中的段選擇器指向的不是相容程式碼段、非相容程式碼段、呼叫門、任務門或任務狀態段的段描述符。如果非相容程式碼段的 DPL 與 CPL 不相等(不使用呼叫門時)。如果段的段選擇器的 RPL 大於 CPL。如果相容程式碼段的 DPL 大於 CPL。如果呼叫門、任務門或 TSS 段描述符的 DPL 小於 CPL,或者小於呼叫門、任務門或 TSS 的段選擇器的 RPL。如果呼叫門中的選擇器的段描述符指出它不是程式碼段。如果任務門中的段選擇器的段描述符未指出可用的 TSS。如果 TSS 的段選擇器將自己的區域性/全域性位設定為區域性。如果 TSS 段描述符指出 TSS 忙或不可用。
#SS(0) - 如果記憶體運算元有效地址超出 SS 段限制。
#NP(選擇器) - 如果訪問的程式碼段不存在。如果呼叫門、任務門或 TSS 不存在。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#AC(0) - 如果啟用對齊檢查並在目前特權級別為 3 時進行未對齊的記憶體引用。(僅在從記憶體獲取目標時發生)。
#GP - 如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。
#SS - 如果記憶體運算元有效地址超出 SS 段限制。
#GP(0) - 如果目標運算元超出程式碼段限制。如果記憶體運算元有效地址超出 CS、DS、ES、FS 或 GS 段限制。
#SS(0) - 如果記憶體運算元有效地址超出 SS 段限制。
#PF(錯誤程式碼) - 如果發生頁錯誤。
#AC(0) - 如果在啟用對齊檢查的情況下進行未對齊的記憶體引用。(僅在從記憶體獲取目標時發生)。