| | 1 | = Exception handling = |
| | 2 | The manual has some information on this. Here's some more. |
| | 3 | |
| | 4 | == UUOs (by gb) == |
| | 5 | |
| | 6 | === Origin === |
| | 7 | On the DEC PDP-10, the acronym UUO stood for "Unimplemented User Operation"; UUOs were used to invoke system services and/or to invoke what we'd think of as exception handlers. [...] As it's used in CCL, the term just means "an illegal instruction that'll ultimately cause a lisp-aware handler function to be called." |
| | 8 | |
| | 9 | === Implementation details === |
| | 10 | Executing an "int $n" (#xcd n) instruction transfers control to an |
| | 11 | entry insome (incredibly complex and baroque) table of handlers |
| | 12 | maintained by the chip. The OS might use a few entries in this table |
| | 13 | to implement system calls (though modern x86 CPUs often have faster |
| | 14 | ways of doing system calls), and some entries are reserved for hardware- |
| | 15 | related interrupts. |
| | 16 | |
| | 17 | Trying to execute an "int $n" instruction that doesn't have a defined |
| | 18 | entry in this table causes a General Protection Fault, and this |
| | 19 | eventually causes (on Unix) a signal to be raised. Exactly what |
| | 20 | signal depends on the OS; there's a macro called SIGNUM_FOR_INTN_TRAP |
| | 21 | defined conditionally in ccl/lisp-kernel/x86-exceptions.h that defines |
| | 22 | this. As you can see, it's generally overloaded on SIGBUS or SIGSEGV, |
| | 23 | which usually indicates an addressing exception (yes, SIGILL would |
| | 24 | make more sense, but since it involves a GP fault an undefined "int |
| | 25 | $n" winds up taking the same path as a memory protection fault) and |
| | 26 | we need to look at other arguments to the signal handler (as defined |
| | 27 | by the IS_MAYBE_INT_TRAP macro: if IS_MAYBE_INT_TRAP is true, we believe |
| | 28 | that we can look at the the byte that the PC in the signal context is |
| | 29 | pointing to to see if it's #xcd; if not, the SIGSEGV or SIGBUS might |
| | 30 | have been caused by trying to execute code at an unmapped address.) |
| | 31 | |
| | 32 | We also use "ud2a" instructions (followed by a byte or more of data) |
| | 33 | for similar purposes, but "int $n" is generally shorter; illegal |
| | 34 | instructions that use the ud2a opcode (which generally -does- raise |
| | 35 | SIGILL when executed) are sometimes called "xuuos" (eXtended UUOs). |
| | 36 | |
| | 37 | Once in a while (when doing a port to a new OS), we've found that the |
| | 38 | OS uses an interrupt that we've previously considered free for our |
| | 39 | use, and have had to do things differently (reassign UUOs or avoid |
| | 40 | emitting conflicting ones.) I suppose that that could happen if we |
| | 41 | ported to some other OS (NetBSD ?), but it's unlikely that x8632 OSes |
| | 42 | will invoke new functionality via "int $n" (since other means of doing |
| | 43 | system calls are generally faster/better.) See the comment in x86-asm.lisp: |
| | 44 | |
| | 45 | {{{;;; DON'T use #xcd8x: doing so will make Mach angry and confused.}}} |