JMP - 跳转

来源:互联网 发布:阿国网络 编辑:程序博客网 时间:2024/06/10 06:09

JMP - 跳转

操作码

指令

说明

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 指令可用于执行以下三种类型的远跳转:

  1. 远跳转到相容或非相容代码段。

  2. 通过调用门进行的远跳转。

  3. 任务切换。

(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 段限制。

虚 8086 模式异常

#GP(0) - 如果目标操作数超出代码段限制。如果内存操作数有效地址超出 CS、DS、ES、FS 或 GS 段限制。

#SS(0) - 如果内存操作数有效地址超出 SS 段限制。

#PF(错误代码) - 如果发生页错误。

#AC(0) - 如果在启用对齐检查的情况下进行未对齐的内存引用。(仅在从内存获取目标时发生)。

原创粉丝点击