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