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).