20. Protection mutator context

20.1. Introduction

.intro: This is the design of the protection mutator context module.

.readership: Any MPS developer; anyone porting the MPS to a new platform.

.overview: The protection mutator context module decodes the context of a mutator thread at the point when it caused a protection fault, so that access to a protected region of memory can be handled, or when it was suspended by the thread manager, so that its registers and control stack can be scanned.

.def.context: The context of a thread (also called its continuation) is an abstract representation of the control state of the thread at a point in time, including enough information to continue the thread from that point.

.status: The protection mutator context module does not currently present a clean interface to the rest of the MPS: source files are inconsistently named, and the implementation is (necessarily) mixed up with the implementation of the memory protection module (design.mps.prot) and the thread manager (design.mps.thread-manager).

20.2. Requirements

.req.fault.addr: Must determine the address that the mutator was trying to access when it caused a protection fault. (Without this address the MPS can’t handle the fault. See ArenaAccess().)

.req.fault.access: Should determine whether the mutator was trying to read or write the address when it caused a protection fault. (This enables a performance improvement in the case of a write fault. A read fault must be handled by ensuring the address pointed to has been fixed, which may require scanning the segment, whereas a write fault merely requires that the segment’s summary be discarded. See TraceSegAccess().)

.req.fault.step: Should be able to emulate the access that caused the fault. (This enables a significant performance improvement for weak hash tables. See request.dylan.160044.)

.req.suspend.scan: Must capature enough information to ambiguously scan all roots in the context of a thread that has been suspended by the thread manager. (This is necessary for conservative garbage collection to work. See design.mps.thread-manager.if.scan.)

20.3. Interface

MutatorFaultContextStruct *MutatorFaultContext

.if.context: A structure representing the context of the mutator at the point when a protection fault occurred, or when it was suspended by the thread manager. This structure should be declared in a header so that it can be inlined in the Thread structure if necessary. See design.mps.thread-manager.if.thread.

Bool ProtCanStepInstruction(MutatorFaultContext context)

.if.canstep: Examine the context to determine whether the protection module can single-step the instruction which is causing the fault. Return TRUE if ProtStepInstruction() is capable of single-stepping the instruction, or FALSE if not.

Bool Res ProtStepInstruction(MutatorFaultContext context)

.if.step: Single-step the instruction which is causing the fault. Update the mutator context according to the emulation or execution of the instruction, so that resuming the mutator will not cause the instruction which was caused the fault to be re-executed. Return ResOK if the instruction was single-stepped successfully, or ResUNIMPL if the instruction cannot be single-stepped.

This function is only called if ProtCanStepInstruction(context) returned TRUE.

Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext context, mps_area_scan_t scan, void *closure)

.if.context.scan: Scan all roots found in context using the given scan state by calling scan, and return the result code from the scanner.

Addr MutatorFaultContextSP(MutatorFaultContext context)

.if.context.sp: Return the pointer to the “top” of the thread’s stack at the point given by context. In the common case, where the stack grows downwards, this is actually the lowest stack address.

20.4. Implementations

20.4.1. Generic implementation

.impl.an: In prmcan.c.

.impl.an.context: There is no definition of MutatorFaultContextStruct and so the mutator context cannot be decoded.

.impl.an.fault: Compatible only with the generic memory protection module (design.mps.prot.impl.an) where there are no protection faults.

.impl.an.suspend: Compatible only with the generic thread manager module (design.mps.thread-manager.impl.an) where there is only one thread, and so no threads are suspended.

20.4.2. Unix implementation

.impl.ix: In protsgix.c, with processor-specific parts in proti3.c and proti6.c, and other platform-specific parts in prmci3fr.c, prmci3li.c, prmci6fr.c, and prmci6li.c.

.impl.ix.context: The context consists of the siginfo_t and ucontext_t structures. POSIX specifies some of the fields in siginfo_t, but says nothing about the contents of ucontext_t. This is decoded on a platform-by-platform basis.

.impl.ix.fault.addr: POSIX specifies that siginfo_t.si_addr is the address that the faulting instruction was attempting to access.

.impl.ix.fault.mode: This implementation does not attempt to determine whether the fault was a read or write.

.impl.ix.fault.step: This is implemented only on IA-32, and only for “simple MOV” instructions.

.impl.ix.suspend: PThreadextSuspend() records the context of each suspended thread, and ThreadRingSuspend() stores this in the Thread structure.

.impl.ix.context.scan: The context’s root registers are found in the ucontext_t.uc_mcontext structure.

.impl.ix.context.sp: The stack pointer is obtained from ucontext_t.uc_mcontext.mc_esp (FreeBSD on IA-32), uc_mcontext.gregs[REG_ESP] (Linux on IA-32), ucontext_t.uc_mcontext.mc_rsp (FreeBSD on x86-64), or uc_mcontext.gregs[REG_RSP] (Linux on x86-64).

20.4.3. Windows implementation

.impl.w3: In proti3.c, proti6.c, prmci3w3.c, and prmci6w3.c.

.impl.w3.context: The context of a thread that hit a protection fault is given by the EXCEPTION_POINTERS structure passed to a vectored exception handler, which points to EXCEPTION_RECORD and CONTEXT structures.

.impl.w3.fault.addr: EXCEPTION_RECORD.ExceptionAddress is the address that the faulting instruction was trying to access.

.impl.w3.fault.mode: EXCEPTION_RECORD.ExceptionInformation[0] is 0 for a read fault, 1 for a write fault, and 8 for an execute fault (which we handle as a read fault).

.impl.w3.fault.step: This is implemented only on IA-32, and only for “simple MOV” instructions.

.impl.w3.suspend: The context of a suspended thread is returned by GetThreadContext().

.impl.w3.context.scan: The context’s root registers are found in the CONTEXT structure.

.impl.w3.context.sp: The stack pointer is obtained from CONTEXT.Esp (on IA-32) or CONTEXT.Rsp (on x86-64).

20.4.4. OS X implementation

.impl.xc: In protxc.c, with processor-specific parts in proti3.c and proti6.c, and other platform-specific parts in prmci3xc.c and prmci6xc.c.

.impl.xc.context: The context consists of the __Request__mach_exception_raise_state_identity_t and x86_thread_state32_t or x86_thread_state64_t structures. There doesn’t seem to be any documentation for these structures, but they are defined in the Mach headers.

.impl.xc.fault.addr: __Request__mach_exception_raise_state_identity_t.code[1] is the address that the faulting instruction was trying to access.

.impl.xc.fault.mode: This implementation does not attempt to determine whether the fault was a read or write.

.impl.xc.fault.step: This is implemented only on IA-32, and only for “simple MOV” instructions.

.impl.xc.suspend: The context of a suspended thread is obtained by calling thread_get_state().

.impl.xc.context.scan: The thread’s registers are found in the x86_thread_state32_t or x86_thread_state64_t structure.

.impl.xc.context.sp: The stack pointer is obtained from x86_thread_state32_t.__esp (on IA-32) or x86_thread_state64_t.__rsp (on x86-64).