INT n/INTO/INT 3 - 调用中断过程
INT n/INTO/INT 3 - 调用中断过程
操作码 |
指令 |
说明 |
CC |
INT 3 |
中断 3 - 调试器陷阱 |
CD ib |
INT imm8 |
立即数字节指定的中断矢量编号 |
CE |
INTO |
中断 4 - 如果上溢标志是 1 |
说明
INT n 指令生成对目标操作数指定的中断或异常处理程序的调用(请参阅“IA-32 英特尔(R) 体系结构软件开发人员手册”第 1 卷第 6 章中标题为“中断与异常”的部分)。目标操作数指定从 0 到 255 的中断矢量编号,编码形式是 8 位无符号立即数。每个中断矢量编号提供一个指向 IDT 中的门描述符的索引。头 32 个中断矢量编号由英特尔(R) 保留供系统使用。其中一些中断用于内部生成的异常。
INT n 指令是通用助记符,用于执行软件生成的中断处理程序调用。INTO 指令是专用助记符,用于调用上溢异常 (#OF),即中断矢量编号 4。上溢中断检查 EFLAGS 寄存器中的 OF 标志,如果 OF 标志设置为 1,则调用上溢中断处理程序。
INT 3 指令生成特殊的单字节操作码 (CC),用于调用调试异常处理程序。(这个单字节形式可用于将任何指令的第一个字节替换成断点或其它单字节指令,而不会覆盖其它代码,因此非常有用)。为进一步支持它作为调试断点的功能,CC 操作码生成的中断同常规的软件中断还存在以下不同:
在 VME 模式中,不会发生中断重定向;中断由保护模式处理程序处理。不发生虚 8086 模式 IOPL 检查。中断可以在任何 IOPL 级别执行,而不触发错误。
请注意,INT 3 的“普通”双字节操作码 (CD03) 没有这些特殊功能。英特尔(R) 与 Microsoft* 汇编器不会从任何助记符生成 CD03 操作码,但是此操作码可以使用直接的数值代码定义或自我修改代码创建。
INT n 指令(包括 INTO 与 INT 3 指令)的操作与使用 CALL 指令进行的远调用类似。主要差异在于,使用 INT n 指令时,会将 EFLAGS 寄存器压入堆栈,放在返回地址的前面。(返回地址是 CS 与 EIP 寄存器的当前值组成的远地址)。从中断过程的返回使用 IRET 指令处理,它从堆栈弹出 EFLAGS 信息与返回地址。
中断矢量编号指定中断描述符表格(IDT)中的中断描述符;也就是说,它提供对 IDT 的索引。所选的中断描述符又包含指向中断或异常处理过程的指针。在保护模式中,IDT 包含一个阵列的 8 字节描述符,每个都是一个中断门、陷阱门或任务门。在实地址模式中,IDT 是一个阵列的 4 字节远指针(2 字节代码段选择器与 2 字节指令指针),每个都直接指向所选段中的过程。(请注意,在实地址模式中,IDT 称为中断矢量表格,它的指针称为中断矢量)。
以下决策表指示在表格的上半部分给定的条件下,执行表格下半部分中的哪项操作。决策表下半部分中的每个 Y 代表此指令(#GP 除外)的“操作”部分定义的过程。
PE |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
VM |
- |
- |
- |
- |
- |
0 |
1 |
1 |
IOPL |
- |
- |
- |
- |
- |
- |
<3 |
=3 |
DPL/CPL 关系 |
- |
DPL< CPL |
- |
DPL> CPL |
DPL= CPL 或 C |
DPL< CPL 与 NC |
- |
- |
中断类型 |
- |
S/W |
- |
- |
- |
- |
- |
- |
门类型 |
- |
- |
任务 |
陷阱或中断 |
陷阱或中断 |
陷阱或中断 |
陷阱或中断 |
陷阱或中断 |
实地址模式 |
Y |
|
|
|
|
|
|
|
保护模式 |
|
Y |
Y |
Y |
Y |
Y |
Y |
Y |
陷阱或中断门 |
|
|
|
Y |
Y |
Y |
Y |
Y |
特权级别间中断 |
|
|
|
|
|
Y |
|
|
特权级别内中断 |
|
|
|
|
Y |
|
|
|
虚 8086 模式中断 |
|
|
|
|
|
|
|
Y |
任务门 |
|
|
Y |
|
|
|
|
|
#GP |
|
Y |
|
Y |
|
|
Y |
|
备注:- 表示无意义;Y 表示执行操作;空白表示不执行操作。
处理器在虚 8086 模式中执行时,IOPL 确定 INT n 指令的操作。如果 IOPL 小于 3,则处理器会生成一般保护性异常 (#GP);如果 IOPL 是 3,则处理器执行特权级别 0 的保护模式中断。要执行特权级别 0 的保护模式中断,中断门的 DPL 必须设置为三,且中断处理过程的目标 CPL 必须是 0。
中断描述符表格寄存器 (IDTR) 指定 IDT 的线性基址与限制。处理器加电或复位之后,IDTR 的初始基址值是 0。
操作
以下操作说明不仅适用于 INT n 与 INTO 指令,也适用于外部中断与异常。
IF PE=0
THEN
GOTO REAL-ADDRESS-MODE;
ELSE (* PE=1 *)
IF (VM=1 AND IOPL < 3 AND INT n)
THEN
#GP(0);
ELSE (* protected mode or virtual-8086 mode interrupt *)
GOTO PROTECTED-MODE;
FI;
FI;
REAL-ADDRESS-MODE:
IF ((DEST * 4) + 3) is not within IDT limit THEN #GP; FI;
IF stack not large enough for a 6-byte return information THEN #SS; FI;
Push (EFLAGS[15:0]);
IF 0; (* Clear interrupt flag *)
TF 0; (* Clear trap flag *)
AC 0; (*Clear AC flag*)
Push(CS);
Push(IP);
(* No error codes are pushed *)
CS IDT(Descriptor (vector_number * 4), selector));
EIP IDT(Descriptor (vector_number * 4), offset)); (* 16 bit offset AND 0000FFFFH *)
END;
PROTECTED-MODE:
IF ((DEST * 8) + 7) is not within IDT limits
OR selected IDT descriptor is not an interrupt-, trap-, or task-gate type
THEN #GP((DEST * 8) + 2 + EXT);
(* EXT is bit 0 in error code *)
FI;
IF software interrupt (* generated by INT n, INT 3, or INTO *)
THEN
IF gate descriptor DPL < CPL
THEN #GP((vector_number * 8) + 2 );
(* PE=1, DPL<CPL, software interrupt *)
FI;
FI;
IF gate not present THEN #NP((vector_number * 8) + 2 + EXT); FI;
IF task gate (* specified in the selected interrupt table descriptor *)
THEN GOTO TASK-GATE;
ELSE GOTO TRAP-OR-INTERRUPT-GATE; (* PE=1, trap/interrupt gate *)
FI;
END;
TASK-GATE: (* PE=1, task gate *)
Read segment selector in task gate (IDT descriptor);
IF local/global bit is set to local
OR index not within GDT limits
THEN #GP(TSS selector);
FI;
Access TSS descriptor in GDT;
IF TSS descriptor specifies that the TSS is busy (low-order 5 bits set to 00001)
THEN #GP(TSS selector);
FI;
IF TSS not present
THEN #NP(TSS selector);
FI;
SWITCH-TASKS (with nesting) to TSS;
IF interrupt caused by fault with error code
THEN
IF stack limit does not allow push of error code
THEN #SS(0);
FI;
Push(error code);
FI;
IF EIP not within code segment limit
THEN #GP(0);
FI;
END;
TRAP-OR-INTERRUPT-GATE
Read segment selector for trap or interrupt gate (IDT descriptor);
IF segment selector for code segment is null
THEN #GP(0H + EXT); (* null selector with EXT flag set *)
FI;
IF segment selector is not within its descriptor table limits
THEN #GP(selector + EXT);
FI;
Read trap or interrupt handler descriptor;
IF descriptor does not indicate a code segment
OR code segment descriptor DPL > CPL
THEN #GP(selector + EXT);
FI;
IF trap or interrupt gate segment is not present,
THEN #NP(selector + EXT);
FI;
IF code segment is non-conforming AND DPL < CPL
THEN IF VM=0
THEN
GOTO INTER-PRIVILEGE-LEVEL-INTERRUPT;
(* PE=1, interrupt or trap gate, nonconforming *)
(* code segment, DPL<CPL, VM=0 *)
ELSE (* VM=1 *)
IF code segment DPL 0 THEN #GP(new code segment selector); FI;
GOTO INTERRUPT-FROM-VIRTUAL-8086-MODE;
(* PE=1, interrupt or trap gate, DPL<CPL, VM=1 *)
FI;
ELSE (* PE=1, interrupt or trap gate, DPL CPL *)
IF VM=1 THEN #GP(new code segment selector); FI;
IF code segment is conforming OR code segment DPL CPL
THEN
GOTO INTRA-PRIVILEGE-LEVEL-INTERRUPT;
ELSE
#GP(CodeSegmentSelector + EXT);
(* PE=1, interrupt or trap gate, nonconforming *)
(* code segment, DPL>CPL *)
FI;
FI;
END;
INTER-PREVILEGE-LEVEL-INTERRUPT
(* PE=1, interrupt or trap gate, non-conforming code segment, DPL<CPL *)
(* Check segment selector and descriptor for stack of new privilege level in current TSS *)
IF current TSS is 32-bit TSS
THEN
TSSstackAddress (new code segment DPL * 8) + 4
IF (TSSstackAddress + 7) > TSS limit
THEN #TS(current TSS selector); FI;
NewSS TSSstackAddress + 4;
NewESP stack address;
ELSE (* TSS is 16-bit *)
TSSstackAddress (new code segment DPL * 4) + 2
IF (TSSstackAddress + 4) > TSS limit
THEN #TS(current TSS selector); FI;
NewESP TSSstackAddress;
NewSS TSSstackAddress + 2;
FI;
IF segment selector is null THEN #TS(EXT); FI;
IF segment selector index is not within its descriptor table limits
OR segment selector's RPL DPL of code segment,
THEN #TS(SS selector + EXT);
FI;
Read segment descriptor for stack segment in GDT or LDT;
IF stack segment DPL DPL of code segment,
OR stack segment does not indicate writable data segment,
THEN #TS(SS selector + EXT);
FI;
IF stack segment not present THEN #SS(SS selector+EXT); FI;
IF 32-bit gate
THEN
IF new stack does not have room for 24 bytes (error code pushed)
OR 20 bytes (no error code pushed)
THEN #SS(segment selector + EXT);
FI;
ELSE (* 16-bit gate *)
IF new stack does not have room for 12 bytes (error code pushed)
OR 10 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
FI;
IF instruction pointer is not within code segment limits THEN #GP(0); FI;
SS:ESP TSS(NewSS:NewESP) (* segment descriptor information also loaded *)
IF 32-bit gate
THEN
CS:EIP Gate(CS:EIP); (* segment descriptor information also loaded *)
ELSE (* 16-bit gate *)
CS:IP Gate(CS:IP); (* segment descriptor information also loaded *)
FI;
IF 32-bit gate
THEN
Push(far pointer to old stack); (* old SS and ESP, 3 words padded to 4 *);
Push(EFLAGS);
Push(far pointer to return instruction); (* old CS and EIP, 3 words padded to 4*);
Push(ErrorCode); (* if needed, 4 bytes *)
ELSE(* 16-bit gate *)
Push(far pointer to old stack); (* old SS and SP, 2 words *);
Push(EFLAGS(15..0]);
Push(far pointer to return instruction); (* old CS and IP, 2 words *);
Push(ErrorCode); (* if needed, 2 bytes *)
FI;
CPL CodeSegmentDescriptor(DPL);
CS(RPL) CPL;
IF interrupt gate
THEN IF 0 (* interrupt flag to 0 (disabled) *); FI;
TF 0;
VM 0;
RF 0;
NT 0;
END;
INTERRUPT-FROM-VIRTUAL-8086-MODE:
(* Check segment selector and descriptor for privilege level 0 stack in current TSS *)
IF current TSS is 32-bit TSS
THEN
TSSstackAddress (new code segment DPL * 8) + 4
IF (TSSstackAddress + 7) > TSS limit
THEN #TS(current TSS selector); FI;
NewSS TSSstackAddress + 4;
NewESP stack address;
ELSE (* TSS is 16-bit *)
TSSstackAddress (new code segment DPL * 4) + 2
IF (TSSstackAddress + 4) > TSS limit
THEN #TS(current TSS selector); FI;
NewESP TSSstackAddress;
NewSS TSSstackAddress + 2;
FI;
IF segment selector is null THEN #TS(EXT); FI;
IF segment selector index is not within its descriptor table limits
OR segment selector's RPL DPL of code segment,
THEN #TS(SS selector + EXT);
FI;
Access segment descriptor for stack segment in GDT or LDT;
IF stack segment DPL DPL of code segment,
OR stack segment does not indicate writable data segment,
THEN #TS(SS selector + EXT);
FI;
IF stack segment not present THEN #SS(SS selector+EXT); FI;
IF 32-bit gate
THEN
IF new stack does not have room for 40 bytes (error code pushed)
OR 36 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
ELSE (* 16-bit gate *)
IF new stack does not have room for 20 bytes (error code pushed)
OR 18 bytes (no error code pushed);
THEN #SS(segment selector + EXT);
FI;
FI;
IF instruction pointer is not within code segment limits THEN #GP(0); FI;
tempEFLAGS EFLAGS;
VM 0;
TF 0;
RF 0;
IF service through interrupt gate THEN IF 0; FI;
TempSS SS;
TempESP ESP;
SS:ESP TSS(SS0:ESP0); (* Change to level 0 stack segment *)
(* Following pushes are 16 bits for 16-bit gate and 32 bits for 32-bit gates *)
(* Segment selector pushes in 32-bit mode are padded to two words *)
Push(GS);
Push(FS);
Push(DS);
Push(ES);
Push(TempSS);
Push(TempESP);
Push(TempEFlags);
Push(CS);
Push(EIP);
GS 0; (*segment registers nullified, invalid in protected mode *)
FS 0;
DS 0;
ES 0;
CS Gate(CS);
IF OperandSize=32
THEN
EIP Gate(instruction pointer);
ELSE (* OperandSize is 16 *)
EIP Gate(instruction pointer) AND 0000FFFFH;
FI;
(* Starts execution of new routine in Protected Mode *)
END;
INTRA-PRIVILEGE-LEVEL-INTERRUPT:
(* PE=1, DPL CPL or conforming segment *)
IF 32-bit gate
THEN
IF current stack does not have room for 16 bytes (error code pushed)
OR 12 bytes (no error code pushed); THEN #SS(0);
FI;
ELSE (* 16-bit gate *)
IF current stack does not have room for 8 bytes (error code pushed)
OR 6 bytes (no error code pushed); THEN #SS(0);
FI;
IF instruction pointer not within code segment limit THEN #GP(0); FI;
IF 32-bit gate
THEN
Push (EFLAGS);
Push (far pointer to return instruction); (* 3 words padded to 4 *)
CS:EIP Gate(CS:EIP); (* segment descriptor information also loaded *)
Push (ErrorCode); (* if any *)
ELSE (* 16-bit gate *)
Push (FLAGS);
Push (far pointer to return location); (* 2 words *)
CS:IP Gate(CS:IP); (* segment descriptor information also loaded *)
Push (ErrorCode); (* if any *)
FI;
CS(RPL) CPL;
IF interrupt gate
THEN
IF 0; FI;
TF 0;
NT 0;
VM 0;
RF 0;
FI;
END;
影响的标志
EFLAGS 寄存器压入堆栈。根据执行 INT 指令时处理器的操作模式,可能会清除 IF、TF、NT、AC、RF 及 VM 标志(请参阅“操作”部分)。如果中断使用任务门,则可能会设置或清除任何标志,具体由新任务的 TSS 中的 EFLAGS 映像控制。
保护模式异常
#GP(0) - 如果 IDT、中断门、陷阱门或任务门中的中断指针超出代码段限制。
#GP(选择器) - 如果中断门、陷阱门或任务门中的段选择器为空。如果中断门、陷阱门、任务门、代码段或 TSS 段选择器索引超出描述符表格限制。如果中断矢量编号超出 IDT 限制。如果 IDT 描述符不是中断描述符、陷阱描述符或任务描述符。如果中断由 INT n、INT 3 或 INTO 指令生成,且中断描述符、陷阱描述符或任务描述符的 DPL 小于 CPL。如果中断门或陷阱门中的段选择器不指向代码段的段描述符。如果 TSS 的段选择器将自己的局部/全局位设置为局部。如果 TSS 段描述符指出 TSS 忙或不可用。
#SS(0) - 如果将返回地址、标志或错误代码压入堆栈时超出堆栈段边界,并且未发生堆栈切换。
#SS(选择器) - 如果加载 SS 寄存器且指向的段标记为不存在。如果发生堆栈切换时,压入的返回地址、标志、错误代码或堆栈段指针超出新堆栈段的边界。
#NP(选择器) - 如果代码段、中断门、陷阱门、任务门或 TSS 不存在。
#TS(选择器) - 如果 TSS 中堆栈段选择器的 RPL 不等于中断门或陷阱门访问的代码段的 DPL。如果 TSS 中堆栈段选择器指向的堆栈段描述符的 DPL 不等于中断门或陷阱门的代码段描述符的 DPL。如果 TSS 中的堆栈段选择器为空。如果 TSS 的堆栈段不是可写的数据段。如果堆栈段的段选择器索引超出描述符表格限制。
#PF(错误代码) - 如果发生页错误。
实地址模式异常
#GP - 如果内存操作数有效地址超出 CS、DS、ES、FS 或 GS 段限制。如果中断矢量编号超出 IDT 限制。
#SS - 如果压入时发生堆栈限制冲突。如果压入的返回地址、标志或错误代码超出堆栈段的边界。
虚 8086 模式异常
#GP(0)(用于 INT n、INTO 或 BOUND 指令)- 如果 IOPL 小于 3 或中断、陷阱、任务门描述符的 DPL 不等于 3。如果 IDT 或中断、陷阱、任务门中的指令指针超出代码段界限。
#GP(选择器) - 如果中断门、陷阱门或任务门中的段选择器为空。如果中断门、陷阱门、任务门、代码段或 TSS 段选择器索引超出描述符表格限制。如果中断矢量编号超出 IDT 限制。如果 IDT 描述符不是中断描述符、陷阱描述符或任务描述符。如果中断由 INT n 指令生成,且中断描述符、陷阱描述符或任务描述符的 DPL 小于 CPL。如果中断门或陷阱门中的段选择器不指向代码段的段描述符。如果 TSS 的段选择器将自己的局部/全局位设置为局部。
#SS(选择器) - 如果加载 SS 寄存器且指向的段标记为不存在。如果压入的返回地址、标志、错误代码、堆栈段指针或数据段超出堆栈段的边界。
#NP(选择器) - 如果代码段、中断门、陷阱门、任务门或 TSS 不存在。
#TS(选择器) - 如果 TSS 中堆栈段选择器的 RPL 不等于中断门或陷阱门访问的代码段的 DPL。如果 TSS 的堆栈段的堆栈段描述符的 DPL 不等于中断门或陷阱门的代码段描述符的 DPL。如果 TSS 中的堆栈段选择器为空。如果 TSS 的堆栈段不是可写的数据段。如果堆栈段的段选择器索引超出描述符表格限制。
#PF(错误代码) - 如果发生页错误。
#BP - 如果执行 INT 3 指令。
#OF - 如果执行 INTO 指令,且 OF 标志设置为 1