.. mode: -*- rst -*- Mutator context =============== :Tag: design.mps.prmc :Author: Gareth Rees :Date: 2014-10-23 :Status: complete design :Revision: $Id: //info.ravenbrook.com/project/mps/master/design/prmc.txt#24 $ :Copyright: See `Copyright and License`_. :Index terms: pair: mutator context; design 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_). .. _design.mps.prot: prot .. _design.mps.thread-manager: thread-manager 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_.) .. _request.dylan.160044: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/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_.) .. _design.mps.thread-manager.if.scan: thread-manager#.if.scan Interface --------- ``typedef unsigned MutatorContextVar`` _`.if.var`: The type ``MutatorContextVar`` is the type of the discriminator for the union within ``MutatorContextStruct``: ======================== ================================================ Value Description ======================== ================================================ ``MutatorContextFAULT`` Context of thread stopped by a protection fault. ``MutatorContextTHREAD`` Context of thread stopped by the thread manager. ======================== ================================================ ``typedef 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_. .. _design.mps.thread-manager.if.thread: thread-manager#.if.thread ``Bool MutatorContextCheck(MutatorContext context)`` _`.if.check`: The check function for mutator contexts. See design.mps.check_. .. _design.mps.check: check ``Res MutatorContextInitFault(MutatorContext context, ...)`` _`.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 ``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. ``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. Implementations --------------- 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. .. _design.mps.prot.impl.an: prot#.impl.an _`.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#.impl.an 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``, ``prmcfri6.c``, ``prmclia6.c``, ``prmclii3.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. .. |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 _`.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). 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. .. |EXCEPTION_POINTERS| replace:: ``EXCEPTION_POINTERS`` .. _EXCEPTION_POINTERS: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_pointers .. |EXCEPTION_RECORD| replace:: ``EXCEPTION_RECORD`` .. _EXCEPTION_RECORD: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_record .. |CONTEXT| replace:: ``CONTEXT`` .. _CONTEXT: https://docs.microsoft.com/en-gb/windows/desktop/api/winnt/ns-winnt-_exception_record _`.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|_. .. |GetThreadContext| replace:: ``GetThreadContext()`` .. _GetThreadContext: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-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). 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 ``prmcxca6.c``, ``prmcxci3.c`` and ``prmcxci6.c``. _`.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. _`.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|_. .. |thread_get_state| replace:: ``thread_get_state()`` .. _thread_get_state: https://www.gnu.org/software/hurd/gnumach-doc/Thread-Execution.html _`.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. _`.impl.xc.context.sp`: The stack pointer is obtained using the ``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). Document History ---------------- - 2014-10-23 GDR_ Initial draft based on design.mps.thread-manager_ and design.mps.prot_. .. _GDR: https://www.ravenbrook.com/consultants/gdr/ Copyright and License --------------------- Copyright © 2014–2020 `Ravenbrook Limited `_. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.