The previous concept found a table of 4096 slots and could not read any of them. This one asks what the first thirty-two are for, and the answer is more interesting than a list.
Two reasons to care. First, a number is not a name: vector 13 is #GP and vector 14 is #PF, and the difference between them changes whether your program's memory error is recoverable. Second — and this is the part that will cost you an afternoon if you do not know it — eight of the thirty-two mean different things depending on which company wrote the chip. Not “are spelled differently.” Different. One of them is a virtualization exception on Intel and a reserved vector on AMD; another is reserved on Intel and a hypervisor-injection exception on AMD. A kernel that reads the Intel table and runs on an AMD part has a bug that will appear on hardware that is not in the room.
Every vector 0–31 is one of three kinds, and the kind determines where the saved instruction pointer points. That single fact is the reason the class exists as a separate column:
So when you read a manual that says “divide error”, the question you actually need answered is can I retry this? — and the answer lives in a column most tables put in small type.
$ ./privbench | sed -n '/vec name cls/,/^ 19/p' vec name cls errcode note 0 #DE F no divide error 1 #DB T no debug; class depends on the cause 2 NMI I no not an exception 3 #BP T no INT3; RIP points at the NEXT instruction 6 #UD F no invalid opcode; RIP points at the FAULTING instruction 8 #DF A yes abort; the saved RIP is UNDEFINED 14 #PF F yes page fault; error code has its OWN format 16 #MF F no x87; saved RIP is the NEXT DEFERRED point, not the fault 17 #AC F yes alignment check 19 #XM F no SIMD FP; AMD calls it #XF
Three rows in that extract are worth more attention than the other thirty.
Vector 1, #DB, has no single class. A debug register that fires on an instruction fetch is a fault, so you may retry; one that fires on a data access is a trap, so you may not. The class is a function of the cause, and the manual's class column for vector 1 is not a simplification — it is the honest answer.
Vector 8, #DF, is the one that is not a number at all in the useful sense. It is a class of its own, it pushes an error code that is always zero, and its saved RIP is undefined because the double fault means the machine could not even complete the bookkeeping for the first one. Worse, if a second fault occurs while the double fault is being delivered, the processor shuts down. There is no third state. If your operating system's fault handler is itself faulting, you get exactly two chances and then the machine stops — which is the entire reason the architecture has this vector and the entire reason the “use a separate stack, via the IST field” mechanism exists.
Vector 16, #MF, is a trap whose saved RIP points somewhere unexpected. The manual is unusually explicit: the saved RIP is the x87 instruction that was about to be executed when the error condition was generated, which is not the instruction in which the error was detected. The real faulting address is in the FPU's own instruction-pointer register. This is a three-way distinction — the CPU pushed one address, the fault was in another, and the difference is in a floating-point register — and it is the clearest example in the whole architecture of a saved RIP that will not mean what you assume it means.
The full table, with the disagreements marked in place:
$ ./privbench | sed -n '/vec name cls/,/defined on both/p'
9 - - no coprocessor segment overrun; RESERVED
15 - - no RESERVED
20 #VE F no virtualization: Intel DEFINES IT, AMD says RESERVED
28 - - no RESERVED on Intel; AMD calls it #HV
29 #VC F no VMM communication; Intel's Table 7-1 wrongly says reserved
30 #SX A no security; Intel's Table 7-1 wrongly says reserved
31 - - no RESERVED
defined on both: 19 Intel only: 1 (#20) AMD only: 1 (#28)
There are three distinct kinds of disagreement here, and treating them as one is how people get this wrong.
Hard divergence (vector 20). Intel defines #VE, a virtualization exception raised on an EPT violation when the “EPT-violation #VE” VM-execution control is set. AMD's table says plainly “20 Reserved.” Same number, same slot in the IDT, opposite meanings. A program that installs a #VE handler and enables the control will, on AMD hardware, catch something else entirely — or nothing.
An AMD-only concept (vector 28).#HV, hypervisor injection, has no Intel assignment at all. If you are writing to the intersection of the two architectures, vector 28 is available to you. If you are writing to a specific one, it is not.
An Intel documentation error (vectors 29 and 30). This one is worth dwelling on because it is a different category again. Intel's own Table 7-1 — the table every course quotes — says vectors 22–31 are reserved. But Intel defines #VC (VMM communication) at 29 and #SX (security) at 30 elsewhere in the same volume, in the hypervisor and trusted-execution chapters, and the Linux kernel has X86_TRAP_VC 29 in its own trap-number header. So the summary table in the authoritative manual contradicts the rest of the manual, and the summary table is the one people read.
And one more piece of documentation unreliability, in the vector that never fires, which is almost charming: vector 9's class is stated three different ways by the same manual. The summary table calls it a fault; the reference section calls it an abort; a third table lists it among “benign exceptions.” It is moot — vector 9 has not been generated since the 486-era coprocessor, and AMD documents the condition as raising #GP today — but it is a clean demonstration that citing a table is not the same as citing a fact, and that the right response to a contradiction is to name it rather than pick one.
Reading a table is not the same as watching one happen. The artifact raises six of them on the actual CPU:
$ ./privbench | sed -n '/Now raise some of them/,/does NOT fault/p' Now raise some of them for real. These are ACTUAL faults on THIS cpu: divq by zero (#DE 0) SIGFPE si_code=1 (0x01) idivq by zero (#DE 0) SIGFPE si_code=1 (0x01) ud2 (#UD 6) SIGILL si_code=2 (0x02) int3 (#BP 3) SIGTRAP si_code=128 (0x80) int $0x80 (vector 128) no fault int $0x28 (vector 40) SIGSEGV si_code=128 (0x80)
Read the last two rows together, because they are the demonstration and the prose afterwards is the conclusion.
Vector 128 is inside 32–255, the user-defined range. This kernel owns vector 128 for the i386 system-call ABI, so int $0x80succeeded — it did not fault, it went to a real handler and came back with an answer. Vector 40 is also in the user-defined range, and this kernel has installed nothing there, so the process died.
The user chose the vector in both cases. The kernel decided what happened. That is the division of responsibility, and it is the whole reason the DPL check in the previous concept is a check against the kernel's table rather than against the caller's intent. A process may name a door; it may not choose what is behind it.
And note int3 giving si_code 128 rather than something more specific. si_code is the kernel's translation, and a breakpoint is a deliberate event with no fault classification to report. The error-code concept is about exactly this gap between what the CPU pushed and what a process can see.
#MF specifically, given that its saved RIP is not the faulting instruction.int 0xFF is in the user-defined range so the kernel handles it. But a reserved vector in 0–31 cannot be reached by int at all — write down why, in terms of the DPL the kernel would have had to install.Forward, the doors concept uses the fact you just established — that a process names a vector and the kernel owns it — to explain why SYSCALL is faster than int $0x80: it skips the table entirely, which is exactly why it also skips the DPL check. The error-code concept takes the “error code” column of this table and finds that it is not one format but three, and that #PF's is a bitfield with a bit whose name is a trap.
Backwards, the previous concept gave you the table these numbers index into. And the forward-reference chain is worth naming: the ISA course's opcode map already told you 0F 0B is UD2, a deliberately undefined opcode, and the memory course told you a page fault is the price of a cold access. Both were naming vector 6 and vector 14 without saying so. Every course in this chain touches these numbers; none of them needed to teach them until the course whose subject they are.
Outward: the JVM course teaches a software exception mechanism, and the comparison is exact and instructive. A JVM's exception table is a table of numbers indexed by program counter, and it is a fault/trap distinction in the same shape — a bytecode handler may resume at the same instruction, a Java handler may not. The JVM reimplemented the fault/trap distinction in an object model, which is why Java checked exceptions exist and why “finally” blocks are not optional in the language. Nothing in the JVM knows this table exists; it had no need to.