.. highlight:: none
.. index::
pair: mutator context; design
.. _design-prmc:
Mutator context
===============
.. mps:prefix:: design.mps.prmc
Introduction
------------
:mps:tag:`intro` This is the design of the mutator context module.
:mps:tag:`readership` Any MPS developer; anyone porting the MPS to a new
platform.
:mps:tag:`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.
:mps:tag:`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.
:mps:tag:`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_).
.. _design.mps.prot: prot.html
.. _design.mps.thread-manager: thread-manager.html
Requirements
------------
:mps:tag:`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 :c:func:`ArenaAccess()`.)
:mps:tag:`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
:c:func:`TraceSegAccess()`.)
:mps:tag:`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_.)
.. _request.dylan.160044: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160044/
:mps:tag:`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_.)
.. _design.mps.thread-manager.if.scan: thread-manager.html#design.mps.thread-manager.if.scan
Interface
---------
.. c:type:: unsigned MutatorContextVar
:mps:tag:`if.var` The type :c:type:`MutatorContextVar` is the type of the
discriminator for the union within :c:type:`MutatorContextStruct`:
======================== ================================================
Value Description
======================== ================================================
``MutatorContextFAULT`` Context of thread stopped by a protection fault.
``MutatorContextTHREAD`` Context of thread stopped by the thread manager.
======================== ================================================
.. c:type:: MutatorContextStruct *MutatorContext
:mps:tag:`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 :c:type:`Thread` structure if necessary.
See design.mps.thread-manager.if.thread_.
.. _design.mps.thread-manager.if.thread: thread-manager.html#design.mps.thread-manager.if.thread
.. c:function:: Bool MutatorContextCheck(MutatorContext context)
:mps:tag:`if.check` The check function for mutator contexts. See
design.mps.check_.
.. _design.mps.check: check.html
.. c:function:: Res MutatorContextInitFault(MutatorContext context, ...)
:mps:tag:`if.init.fault` 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 :c:type:`Res` if
this always succeeds.
.. c:function:: Res MutatorContextInitThread(MutatorContext context, ...)
:mps:tag:`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 :c:type:`Res` if
this always succeeds.
.. c:function:: Bool MutatorContextCanStepInstruction(MutatorContext context)
:mps:tag:`if.canstep` Examine the context to determine whether the
protection module can single-step the instruction which is causing the
fault. Return :c:macro:`TRUE` if :c:func:`MutatorContextStepInstruction()` is
capable of single-stepping the instruction, or :c:macro:`FALSE` if not.
.. c:function:: Res MutatorContextStepInstruction(MutatorContext context)
:mps:tag:`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 :c:macro:`TRUE`.
.. c:function:: Res MutatorContextScan(ScanState ss, MutatorContext context, mps_area_scan_t scan, void *closure)
:mps:tag:`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.
.. c:function:: Addr MutatorContextSP(MutatorContext context)
:mps:tag:`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.
Implementations
---------------
Generic implementation
......................
:mps:tag:`impl.an` In ``prmcan.c`` and ``prmcanan.c``.
:mps:tag:`impl.an.context` There is no definition of
:c:type:`MutatorContextStruct` and so the mutator context cannot be decoded.
:mps:tag:`impl.an.fault` Compatible only with the generic memory protection
module (design.mps.prot.impl.an_) where there are no protection
faults.
.. _design.mps.prot.impl.an: prot.html#design.mps.prot.impl.an
:mps:tag:`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.
.. _design.mps.thread-manager.impl.an: thread-manager.html#design.mps.thread-manager.impl.an
Posix implementation
....................
:mps:tag:`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``, ``prmcfri6.c``,
``prmclia6.c``, ``prmclii3.c``, and ``prmclii6.c``.
:mps:tag:`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.
.. |siginfo_t| replace:: ``siginfo_t``
.. _siginfo_t: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
.. |ucontext_t| replace:: ``ucontext_t``
.. _ucontext_t: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html
:mps:tag:`impl.ix.fault.signal` POSIX specifies that "Invalid permissions
for mapped object" (a protection fault) causes a :c:macro:`SEGV` signal.
:mps:tag:`impl.ix.fault.code` POSIX specifies that "Invalid permissions for
mapped object" (a protection fault) causes ``siginfo_t.si_code`` to be
set to :c:macro:`SEGV_ACCERR`.
:mps:tag:`impl.ix.fault.addr` POSIX specifies that ``siginfo_t.si_addr`` is
the address that the faulting instruction was attempting to access.
:mps:tag:`impl.ix.fault.mode` This implementation does not attempt to
determine whether the fault was a read or write.
:mps:tag:`impl.ix.fault.step` This is implemented only on IA-32, and only
for "simple MOV" instructions.
:mps:tag:`impl.ix.suspend` :c:func:`PThreadextSuspend()` records the context of
each suspended thread, and :c:func:`ThreadRingSuspend()` stores this in the
:c:type:`Thread` structure.
:mps:tag:`impl.ix.context.scan` The context's root registers are found in
the ``ucontext_t.uc_mcontext`` structure.
:mps:tag:`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).
Windows implementation
......................
:mps:tag:`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``.
:mps:tag:`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.
.. |EXCEPTION_POINTERS| replace:: :c:macro:`EXCEPTION_POINTERS`
.. _EXCEPTION_POINTERS: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_pointers
.. |EXCEPTION_RECORD| replace:: :c:macro:`EXCEPTION_RECORD`
.. _EXCEPTION_RECORD: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_record
.. |CONTEXT| replace:: :c:macro:`CONTEXT`
.. _CONTEXT: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_record
:mps:tag:`impl.w3.fault.addr` ``EXCEPTION_RECORD.ExceptionAddress`` is the
address that the faulting instruction was trying to access.
:mps:tag:`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).
:mps:tag:`impl.w3.fault.step` This is implemented only on IA-32, and only
for "simple MOV" instructions.
:mps:tag:`impl.w3.suspend` The context of a suspended thread is returned by
|GetThreadContext|_.
.. |GetThreadContext| replace:: :c:func:`GetThreadContext()`
.. _GetThreadContext: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getthreadcontext
:mps:tag:`impl.w3.context.scan` The context's root registers are found in
the |CONTEXT|_ structure.
:mps:tag:`impl.w3.context.sp` The stack pointer is obtained from
``CONTEXT.Esp`` (on IA-32) or ``CONTEXT.Rsp`` (on x86-64).
macOS implementation
....................
:mps:tag:`impl.xc` In ``prmcix.c`` and ``prmcxc.c``, with processor-specific
parts in ``prmci3.c`` and ``prmci6.c``, and other platform-specific
parts in ``prmcxca6.c``, ``prmcxci3.c`` and ``prmcxci6.c``.
:mps:tag:`impl.xc.context` The context consists of the
``__Request__mach_exception_raise_state_identity_t`` and
``arm_thread_state_t``, ``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.
:mps:tag:`impl.xc.fault.addr` ``__Request__mach_exception_raise_state_identity_t.code[1]`` is the
address that the faulting instruction was trying to access.
:mps:tag:`impl.xc.fault.mode` This implementation does not attempt to
determine whether the fault was a read or write.
:mps:tag:`impl.xc.fault.step` This is implemented only on IA-32, and only
for "simple MOV" instructions.
:mps:tag:`impl.xc.suspend` The context of a suspended thread is obtained by
calling |thread_get_state|_.
.. |thread_get_state| replace:: :c:func:`thread_get_state()`
.. _thread_get_state: https://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html
:mps:tag:`impl.xc.context.scan` The thread's registers are found in the
``arm_thread_state64_t``, ``x86_thread_state32_t`` or
``x86_thread_state64_t`` structure.
:mps:tag:`impl.xc.context.sp` The stack pointer is obtained using the
:c:func:`arm_thread_state64_get_sp()` macro (on ARM64), or from
``x86_thread_state32_t.__esp`` (on IA-32) or
``x86_thread_state64_t.__rsp`` (on x86-64).