23. Mutator context¶
23.1. Introduction¶
.intro: This is the design of the mutator context module.
.readership: Any MPS developer; anyone porting the MPS to a new platform.
.overview: The 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 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).
23.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 capture 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.)
23.3. Interface¶
-
unsigned
MutatorContextVar
¶
.if.var: The type MutatorContextVar
is the type of the
discriminator for the union within MutatorContextStruct
:
Value |
Description |
---|---|
|
Context of thread stopped by a protection fault. |
|
Context of thread stopped by the thread manager. |
-
MutatorContextStruct *
MutatorContext
¶
.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
MutatorContextCheck
(MutatorContext context)¶
.if.check: The check function for mutator contexts. See design.mps.check.
-
Res
MutatorContextInitFault
(MutatorContext context, ...)¶
.if.init.thread: Initialize with the context of the mutator at the
point where it was stopped by a protection fault. The arguments are
platform-specific and the return may be void
instead of Res
if
this always succeeds.
-
Res
MutatorContextInitThread
(MutatorContext context, ...)¶
.if.init.thread: Initialize with the context of the mutator at the
point where it was suspended by the thread manager. The arguments are
platform-specific and the return may be void
instead of Res
if
this always succeeds.
-
Bool
MutatorContextCanStepInstruction
(MutatorContext 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 MutatorContextStepInstruction()
is
capable of single-stepping the instruction, or FALSE
if not.
-
Bool Res
MutatorContextStepInstruction
(MutatorContext 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
MutatorContextCanStepInstruction(context)
returned TRUE
.
-
Res
MutatorContextScan
(ScanState ss, MutatorContext 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
MutatorContextSP
(MutatorContext 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.
23.4. Implementations¶
23.4.1. Generic implementation¶
.impl.an: In prmcan.c
and prmcanan.c
.
.impl.an.context: There is no definition of
MutatorContextStruct
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.
23.4.2. Posix implementation¶
.impl.ix: In prmcix.c
and protsgix.c
, with
processor-specific parts in prmci3.c
and prmci6.c
, and other
platform-specific parts in prmcfri3.c
, prmclii3.c
,
prmcfri6.c
, and prmclii6.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.signal: POSIX specifies that “Invalid permissions
for mapped object” (a protection fault) causes a SEGV
signal.
.impl.ix.fault.code: POSIX specifies that “Invalid permissions for
mapped object” (a protection fault) causes siginfo_t.si_code
to be
set to SEGV_ACCERR
.
.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).
23.4.3. Windows implementation¶
.impl.w3: In prmcw3.c
, with processor-specific parts in
prmci3.c
, prmci6.c
, and other platform-specific parts in
prmcw3i3.c
and prmcw3i6.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).
23.4.4. macOS implementation¶
.impl.xc: In prmcix.c
and prmcxc.c
, with processor-specific
parts in prmci3.c
and prmci6.c
, and other platform-specific
parts in prmcxci3.c
and prmcxci6.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).