【译】x86程序员手册35-9.8异常条件
译注:一些异常没有翻译,因为看书时主要为了理解linux代码,所以代码中没有主要使用的就没有仔细看。这部分内容后期再看时再进行翻译。
9.8 Exception Conditions 异常条件
The following sections describe each of the possible exception conditions in detail. Each description classifies the exception as a fault, trap, or abort. This classification provides information needed by systems programmers for restarting the procedure in which the exception occurred:
下面部分详细描述了每个可能的异常条件。每个描述将异常分为错误、陷阱和忽略。这种分类提供系统程序在异常发生时重启程序所需要的信息。
Faults 错误
The CS and EIP values saved when a fault is reported point to the instruction causing the fault.
当错误发生时,指向引起错误的指令的CS和EIP值被保存。
Traps 陷阱
The CS and EIP values stored when the trap is reported point to the instruction dynamically after the instruction causing the trap. If a trap is detected during an instruction that alters program flow, the reported values of CS and EIP reflect the alteration of program flow. For example, if a trap is detected in a JMPinstruction, the CS and EIP values pushed onto the stack point to the target of the JMP, not to the instruction after the JMP.
当陷阱发生时,CS和EIP值被保存,这些值动态指向引发陷阱指令后面的指令。如果执行变更执行流期间陷阱被侦测到,CS和EIP的值反射程序流程的变更。例如,如果陷阱在JMP指令中间被侦测到,压入栈中的CS和EIP值将指向JMP的目的,而不是JMP后面的指令。
Aborts 忽略
An abort is an exception that permits neither precise location of the instruction causing the exception nor restart of the program that caused the exception. Aborts are used to report severe errors, such as hardware errors and inconsistent or illegal values in system tables.
忽略即不精确报告引发异常的指令位置,也不能重启引发异常的程序。错误用来执行致使的错误,例如硬件错误、系统表中不一致或非法的值。
9.8.1 Interrupt 0 -- Divide Error
中断0 —— 除法错误
The divide-error fault occurs during a DIV or an IDIV instruction when the divisor is zero.
在DIV或IDIV指令执行期间,如果除数是0,则发生除法错误。
9.8.2 Interrupt 1 -- Debug Exceptions
中断1 —— 调试异常
The processor triggers this interrupt for any of a number of conditions; whether the exception is a fault or a trap depends on the condition:
在很多条件下处理器触发这个中断;异常是一个错误或一个陷阱要依赖于下列条件:
- Instruction address breakpoint fault. 指令地址断点错误
- Data address breakpoint trap. 数据地址断点陷阱
- General detect fault. 一般性错误
- Single-step trap. 单步陷阱
- Task-switch breakpoint trap. 任务切换断点陷阱
The processor does not push an error code for this exception. An exception handler can examine the debug registers to determine which condition caused the exception . Refer to Chapter 12 for more detailed information about debugging and the debug registers.
处理器不会为这些异常压入错误代码。异常处理程序可以通过调试寄存器来查看是何种情况引发异常。参见第12章关于调试和调试寄存器更多详细信息。
9.8.3 Interrupt 3 -- Breakpoint
The INT 3 instruction causes this trap. The INT 3 instruction is one byte long, which makes it easy to replace an opcode in an executable segment with the breakpoint opcode. The operating system or a debugging subsystem can use a data-segment alias for an executable segment to place an INT 3 anywhere it is convenient to arrest normal execution so that some sort of special processing can be performed. Debuggers typically use breakpoints as a way of displaying registers, variables, etc., at crucial points in a task.
The saved CS:EIP value points to the byte following the breakpoint. If a debugger replaces a planted breakpoint with a valid opcode, it must subtract one from the saved EIP value before returning . Refer also to Chapter 12 for more information on debugging.
9.8.4 Interrupt 4 -- Overflow
This trap occurs when the processor encounters an INTO instruction and the OF (overflow) flag is set. Since signed arithmetic and unsigned arithmetic both use the same arithmetic instructions, the processor cannot determine which is intended and therefore does not cause overflow exceptions automatically. Instead it merely sets OF when the results, if interpreted as signed numbers, would be out of range. When doing arithmetic on signed operands, careful programmers and compilers either test OF directly or use the INTO instruction.
9.8.5 Interrupt 5 -- Bounds Check
This fault occurs when the processor, while executing a BOUND instruction, finds that the operand exceeds the specified limits. A program can use the BOUNDinstruction to check a signed array index against signed limits defined in a block of memory.
9.8.6 Interrupt 6 -- Invalid Opcode
中断6 —— 无效操作
This fault occurs when an invalid opcode is detected by the execution unit. (The exception is not detected until an attempt is made to execute the invalid opcode; i.e., prefetching an invalid opcode does not cause this exception.) No error code is pushed on the stack. The exception can be handled within the same task.
执行单元侦测到一个非法操作时发生此错误。(这个异常直到企图执行一个非法操作时才会被侦测到;也就是说,预先取到的非法操作不会引发这个异常。)没有错误代码入栈。异常可以在同一个任务中处理。
This exception also occurs when the type of operand is invalid for the given opcode. Examples include an intersegment JMP referencing a register operand, or an LESinstruction with a register source operand.
对于给定的操作,当操作数类型无效时也会引发这个异常。例如,一个段内JMP引用一个寄存器操作数,LES指令使用寄存器作为源操作数。
9.8.7 Interrupt 7 -- Coprocessor Not Available
中断7 —— 协处理器不存在
This exception occurs in either of two conditions:
- The processor encounters an ESC (escape) instruction, and the EM (emulate) bit ofCR0 (control register zero) is set.
- The processor encounters either the WAIT instruction or an ESC instruction, and both the MP (monitor coprocessor) and TS (task switched) bits of CR0 are set.
Refer to Chapter 11 for information about the coprocessor interface .
9.8.8 Interrupt 8 -- Double Fault
中断8 —— 双重错误
Normally, when the processor detects an exception while trying to invoke the handler for a prior exception, the two exceptions can be handled serially. If, however, the processor cannot handle them serially, it signals the double-fault exception instead. To determine when two faults are to be signalled as a double fault, the 80386 divides the exceptions into three classes: benign exceptions, contributory exceptions, and page faults. Table 9-3 shows this classification.
通常,在激活一个高优先级异常的处理程序时,如果处理器侦测到一个异常,这两个异常可以被连续地处理。然而,如果处理器不能连续地处理它们,会发送一个双错误的异常信号。决定何时两个错误应当被视为双重错误而发送一个信号,80386将这些异常分为三类:温和异常、促进异常、页错误。表9-3展示这些分类。
Table 9-3. Double-Fault Detection Classes
Class ID Description
1 Debug exceptions
2 NMI
3 Breakpoint
Benign 4 Overflow
Exceptions 5 Bounds check
6 Invalid opcode
7 Coprocessor not available
16 Coprocessor error
0 Divide error
9 Coprocessor Segment Overrun
Contributory 10 Invalid TSS
Exceptions 11 Segment not present
12 Stack exception
13 General protection
Page Faults 14 Page fault
Table 9-4 shows which combinations of exceptions cause a double fault and which do not.
The processor always pushes an error code onto the stack of the double-fault handler; however, the error code is always zero. The faulting instruction may not be restarted. If any other exception occurs while attempting to invoke the double-fault handler, the processor shuts down.
Table 9-4. Double-Fault Definition
SECOND EXCEPTION
Benign Contributory Page
Exception Exception Fault
Benign OK OK OK
Exception
FIRST Contributory OK DOUBLE OK
EXCEPTION Exception
Page
Fault OK DOUBLE DOUBLE
9.8.9 Interrupt 9 -- Coprocessor Segment Overrun
This exception is raised in protected mode if the 80386 detects a page or segment violation while transferring the middle portion of a coprocessor operand to the NPX . This exception is avoidable. Refer to Chapter 11 for more information about the coprocessor interface.
9.8.10 Interrupt 10 -- Invalid TSS
中断10 —— 无效TSS
Interrupt 10 occurs if during a task switch the new TSS is invalid. A TSS is considered invalid in the cases shown in Table 9-5. An error code is pushed onto the stack to help identify the cause of the fault. The EXT bit indicates whether the exception was caused by a condition outside the control of the program; e.g., an external interrupt via a task gate triggered a switch to an invalid TSS.
任务切换时,一个新TSS无效会发生中断10。TSS被视为无效的情况见表9-5。错误代码被入栈帮助识别错误原因。EXT位表明异常是不k由程序外部条件引起;也就是说,外部中断通过任务门触发切换到一个无知TSS。
This fault can occur either in the context of the original task or in the context of the new task. Until the processor has completely verified the presence of the new TSS, the exception occurs in the context of the original task. Once the existence of the new TSS is verified, the task switch is considered complete; i.e., TR is updated and, if the switch is due to a CALL or interrupt, the backlink of the new TSS is set to the old TSS. Any errors discovered by the processor after this point are handled in the context of the new task.
错误即可在原任务下文发生,也可以新任务的上下文发生。直到处理器已经完全验证新TSS存在,异常才会在原任务环境中发生。一旦新TSS被验证存在,任务转换被视为结束;也就是说,TR被更新,如果切换由CALL或中断引起,新TSS的后向连接被设为原TSS。这个时候任何被处理器发现的错误都在新任务环境中被处理。
To insure a proper TSS to process it, the handler for exception 10 must be a task invoked via a task gate.
为确保正确处理TSS,异常10的处理程序必须是一个通过任务门来激活的任务。
Table 9-5. Conditions That Invalidate the TSS
Error Code Condition
TSS id + EXT The limit in the TSS descriptor is less than 103
LTD id + EXT Invalid LDT selector or LDT not present
SS id + EXT Stack segment selector is outside table limit
SS id + EXT Stack segment is not a writable segment
SS id + EXT Stack segment DPL does not match new CPL
SS id + EXT Stack segment selector RPL < > CPL
CS id + EXT Code segment selector is outside table limit
CS id + EXT Code segment selector does not refer to code
segment
CS id + EXT DPL of non-conforming code segment < > new CPL
CS id + EXT DPL of conforming code segment > new CPL
DS/ES/FS/GS id + EXT DS, ES, FS, or GS segment selector is outside
table limits
DS/ES/FS/GS id + EXT DS, ES, FS, or GS is not readable segment
9.8.11 Interrupt 11 -- Segment Not Present
中断11 —— 段不存在
Exception 11 occurs when the processor detects that the present bit of a descriptor is zero. The processor can trigger this fault in any of these cases:
当处理器发现一个描述符的存在位是0时,发生异常11。在下列情况时处理器会触发这个错误:
- While attempting to load the CS, DS, ES, FS, or GS registers; loading the SS register, however, causes a stack fault.
当企图装入CS、DS、ES、FS或GS寄存器;装入SS寄存器,然而引发栈错误。
- While attempting loading the LDT register with an LLDT instruction; loading the LDT register during a task switch operation, however, causes the "invalid TSS" exception.
使用LLDT指令装入LDT寄存器时;在任务切换期间装入LDT寄存器,导致无效TSS异常。
- While attempting to use a gate descriptor that is marked not-present.
当企图使用一个被标记为不存在的门描述符时。
This fault is restartable. If the exception handler makes the segment present and returns, the interrupted program will resume execution.
这个错误是可重启的。如果异常处理程序使段存在并且返回,中断的程序可以被恢复执行。
If a not-present exception occurs during a task switch, not all the steps of the task switch are complete. During a task switch, the processor first loads all the segment registers, then checks their contents for validity. If a not-present exception is discovered, the remaining segment registers have not been checked and therefore may not be usable for referencing memory. The not-present handler should not rely on being able to use the values found in CS, SS, DS, ES, FS, and GS without causing another exception. The exception handler should check all segment registers before trying to resume the new task; otherwise, general protection faults may result later under conditions that make diagnosis more difficult. There are three ways to handle this case:
如果不存在异常发生在任务切换期间,不是所有的任务切换步骤都被完成。任务切换期间,处理器首先装入所有段寄存器,然后再检查它们的内容是否合法。如果存面在异常现,剩余的段寄存器还没有被检查完毕,因此引用的内容可能是不可用的。不存在异常处理程序不应当依赖CS、SS、DS、ES、FS和GS中的找到的值,认为他们不会引发另一个异常。异常处理程序应当在试图恢复新任务之前检查所有段寄存器;否则,可能在后面引发一般性保护错误,在这种情况下,会使诊断更加困难。这种情况下有三种方式进行处理:
- Handle the not-present fault with a task. The task switch back to the interrupted task will cause the processor to check the registers as it loads them from the TSS.
使用栈来处理不存在错误。切换回被中断的任务会促使处理器在从TSS中装入时检查寄存器。
- PUSH and POP all segment registers. Each POP causes the processor to check the new contents of the segment register.
PUSH和POP所有段寄存器。每次POP都会促使处理器检查段寄存器的新内容。
- Scrutinize the contents of each segment-register image in the TSS, simulating the test that the processor makes when it loads a segment register.
仔细检查位于TSS映像中的每个段寄存器内容,当载入段寄存器时,模拟处理器对其进行测试。
This exception pushes an error code onto the stack. The EXT bit of the error code is set if an event external to the program caused an interrupt that subsequently referenced a not-present segment. The I-bit is set if the error code refers to an IDT entry, e.g., an INT instruction referencing a not-present gate.
这个异常会将错误代码压入栈。如果程序外部的事件引起中断,并在接下来引用了一个不存在的段,错误代码的EXT位会被置位。如果错误代码引用一个IDT项,则I位被置位。比如,引用一个不存在门的INT指令。
An operating system typically uses the "segment not present" exception to implement virtual memory at the segment level. A not-present indication in a gate descriptor, however, usually does not indicate that a segment is not present (because gates do not necessarily correspond to segments). Not-present gates may be used by an operating system to trigger exceptions of special significance to the operating system.
操作系统典型地利用段不存在异常来实现段级的虚拟内存。然而,对于门描述符中的不存在标记,通常不表明那个段真不存在(因为门不必要与段一致)。不存在的门可以被操作系统用来触发对于操作系统而言特别重要的异常。
9.8.12 Interrupt 12 -- Stack Exception
中断12 —— 栈异常
A stack fault occurs in either of two general conditions:
- As a result of a limit violation in any operation that refers to the SS register. This includes stack-oriented instructions such as POP, PUSH, ENTER, and LEAVE, as well as other memory references that implicitly use SS (for example, MOV AX, [BP+6]). ENTER causes this exception when the stack is too small for the indicated local-variable space.
- When attempting to load the SS register with a descriptor that is marked not-present but is otherwise valid. This can occur in a task switch, an interlevel CALL, an interlevel return, an LSS instruction, or a MOV or POP instruction to SS.
When the processor detects a stack exception, it pushes an error code onto the stack of the exception handler. If the exception is due to a not-present stack segment or to overflow of the new stack during an interlevel CALL, the error code contains a selector to the segment in question (the exception handler can test the present bit in the descriptor to determine which exception occurred); otherwise the error code is zero.
An instruction that causes this fault is restartable in all cases. The return pointer pushed onto the exception handler's stack points to the instruction that needs to be restarted. This instruction is usually the one that caused the exception; however, in the case of a stack exception due to loading of a not-present stack-segment descriptor during a task switch, the indicated instruction is the first instruction of the new task.
When a stack fault occurs during a task switch, the segment registers may not be usable for referencing memory. During a task switch, the selector values are loaded before the descriptors are checked. If a stack fault is discovered, the remaining segment registers have not been checked and therefore may not be usable for referencing memory. The stack fault handler should not rely on being able to use the values found in CS, SS, DS, ES, FS, and GS without causing another exception. The exception handler should check all segment registers before trying to resume the new task; otherwise, general protection faults may result later under conditions that make diagnosis more difficult.
9.8.13 Interrupt 13 -- General Protection Exception
中断13 —— 一般保护异常
All protection violations that do not cause another exception cause a general protection exception. This includes (but is not limited to):
- Exceeding segment limit when using CS, DS, ES, FS, or GS
- Exceeding segment limit when referencing a descriptor table
- Transferring control to a segment that is not executable
- Writing into a read-only data segment or into a code segment
- Reading from an execute-only segment
- Loading the SS register with a read-only descriptor (unless the selector comes from the TSS during a task switch, in which case a TSS exception occurs
- Loading SS, DS, ES, FS, or GS with the descriptor of a system segment
- Loading DS, ES, FS, or GS with the descriptor of an executable segment that is not also readable
- Loading SS with the descriptor of an executable segment
- Accessing memory via DS, ES, FS, or GS when the segment register contains a null selector
- Switching to a busy task
- Violating privilege rules
- Loading CR0 with PG=1 and PE=0.
- Interrupt or exception via trap or interrupt gate from V86 mode to privilege level other than zero.
- Exceeding the instruction length limit of 15 bytes (this can occur only if redundant prefixes are placed before an instruction)
The general protection exception is a fault. In response to a general protection exception, the processor pushes an error code onto the exception handler's stack. If loading a descriptor causes the exception, the error code contains a selector to the descriptor; otherwise, the error code is null. The source of the selector in an error code may be any of the following:
- An operand of the instruction.
- A selector from a gate that is the operand of the instruction.
- A selector from a TSS involved in a task switch.
9.8.14 Interrupt 14 -- Page Fault
中断14 —— 页错误
This exception occurs when paging is enabled (PG=1) and the processor detects one of the following conditions while translating a linear address to a physical address:
当分页功能启用(PG = 1),处理器在转换线性地址到物理地址时侦测到下面的条件时发生这个异常:
- The page-directory or page-table entry needed for the address translation has zero in its present bit.
地址转换所需要的页目录或页表项,其存在位为0。
- The current procedure does not have sufficient privilege to access the indicated page.
当前程序没有足够的特权级别来访问指明的页。
The processor makes available to the page fault handler two items of information that aid in diagnosing the exception and recovering from it:
对于页错误处理程序而言,处理器使下面的两个信息变得有效,这些信息有助于诊断异常和从中恢复:
- An error code on the stack. The error code for a page fault has a format different from that for other exceptions (see Figure 9-8 ). The error code tells the exception handler three things:
栈上的错误代码。页错误的错误代码从格式上与其他异常的不同(见图9-8)。这个错误代码告诉异常处理程序三个事情:
- Whether the exception was due to a not present page or to an access rights violation.
异常是否由不存在页或一个访问权限违规引起。
- Whether the processor was executing at user or supervisor level at the time of the exception.
当异常发生时处理器是执行在用户级别还是超级用户级别。
- Whether the memory access that caused the exception was a read or write.
引起异常的内存访问是读取还是写入。
- CR2 (control register two). The processor stores in CR2 the linear address used in the access that caused the exception (see Figure 9-9). The exception handler can use this address to locate the corresponding page directory and page table entries. If another page fault can occur during execution of the page fault handler, the handler should push CR2 onto the stack.
CR2(控制寄存器2)。处理器将引起异常的线性地址保存在CR2中(购图9-9)。异常处理程序可以使用这个地址来定位相关页目录和页表项。如果在执行页错误处理程序期间,另一个页错误发生,处理程序应当将CR2压入栈内。
9.8.14.1 Page Fault During Task Switch 任务切换期间的页错误
The processor may access any of four segments during a task switch:
任务切换期间,处理器可以访问以下四个段:
- Writes the state of the original task in the TSS of that task.
将原任务的状态写到它的TSS中。
- Reads the GDT to locate the TSS descriptor of the new task.
读取GDT来定位新任务的TSS描述符。
- Reads the TSS of the new task to check the types of segment descriptors from the TSS.
读新任务的TSS来检查其来自TSS的段描述符类型。
- May read the LDT of the new task in order to verify the segment registers stored in the new TSS.
可以读取新任务的LDT,以便检验新TSS中保存的段寄存器。
A page fault can result from accessing any of these segments. In the latter two cases the exception occurs in the context of the new task. The instruction pointer refers to the next instruction of the new task, not to the instruction that caused the task switch. If the design of the operating system permits page faults to occur during task-switches, the page-fault handler should be invoked via a task gate.
访问这些段中的任何一个都能导致页错误。随后的两个情况,异常发生在新任务的上下文中。指令指针指向新任务的下一条指令,而不是引起任务切换的那条指令。如果操作系统的设计允许页错误在任务切换时发生,页错误处理程序应当通过任务门激活。
9.8.14.2 Page Fault with Inconsistent Stack Pointer
不一致的栈指针导致的页错误
Special care should be taken to ensure that a page fault does not cause the processor to use an invalid stack pointer (SS:ESP). Software written for earlier processors in the 8086 family often uses a pair of instructions to change to a new stack; for example:
要确保页错误不会导致处理器使用一个无效的栈指针(SS:ESP),这一点要特别关注。为早期8086家庭处理器书定的软件经学使用一对指令来修改以指向新栈;例如:
MOV SS, AX
MOV SP, StackTop
With the 80386, because the second instruction accesses memory, it is possible to get a page fault after SS has been changed but before SP has received the corresponding change. At this point, the two parts of the stack pointer SS:SP (or, for 32-bit programs, SS:ESP) are inconsistent.
在80386中,因为第二条指令访问内存,在SS已被修改,而SP还没有接收一个一致性的修改时,是可能引发页错误的。在这点上,栈指针的两个部分SS:SP(或者,对于32位程序,是SS:ESP)是不一致的。
The processor does not use the inconsistent stack pointer if the handling of the page fault causes a stack switch to a well defined stack (i.e., the handler is a task or a more privileged procedure). However, if the page fault handler is invoked by a trap or interrupt gate and the page fault occurs at the same privilege level as the page fault handler, the processor will attempt to use the stack indicated by the current (invalid) stack pointer.
如果页错误的处理导致栈切换到一个定义好的栈(也就是说,处理程序是一个任务或者更高特权级别的程序),处理器不使用这个不一致的栈指针。然而,如果页错误处理程序是被陷阱或中断门激活的,并且页错误发生在与页错误处理程序相同特权级别上,处理器将试图使用当前(无效的)栈指针表明的栈。
In systems that implement paging and that handle page faults within the faulting task (with trap or interrupt gates), software that executes at the same privilege level as the page fault handler should initialize a new stack by using the new LSS instruction rather than an instruction pair shown above. When the page fault handler executes at privilege level zero (the normal case), the scope of the problem is limited to privilege-level zero code, typically the kernel of the operating system.
在实现分页的系统中,在故障任务(使用陷阱或中断门)中处理页错误,与页错误处理程序相同特权等级的软件应当通过使用一个新的LSS指令,而不是上面看到的那对指令来初始化一个新栈。当页错误处理程序在特权级别0(通常做法)上执行时,问题的范围被限定在特权级别0的代码中,最典型的就是操作系统内核。
9.8.15 Interrupt 16 -- Coprocessor Error
中断16 —— 协处理器错误
The 80386 reports this exception when it detects a signal from the 80287 or 80387 on the 80386's ERROR# input pin. The 80386 tests this pin only at the beginning of certain ESC instructions and when it encounters a WAIT instruction while the EM bit of the MSW is zero (no emulation). Refer to Chapter 11 for more information on the coprocessor interface.