Fix the osx exception port forwarding limitations.
authorMike McLaughlin <mikem@microsoft.com>
Wed, 27 Jan 2016 00:37:14 +0000 (16:37 -0800)
committerMike McLaughlin <mikem@microsoft.com>
Fri, 26 Feb 2016 19:42:37 +0000 (11:42 -0800)
Cleanup CThreadMachExceptionHandlerNode/CThreadMachExceptionHandlers.

Send forward exception, save thread info, restore thread back to faulting context and restart faulting instruction and forward.

Add MachExceptionInfo struct used to pass all the exception state around. Bump the IP back one when restarting breakpoint exceptions.

Match lldb exception port options.  Found and fix bug with MACH_EXCEPTION_CODES behavior. Needed pack(4) around the exception message structs.

Cleaned up the _ASSERT, CHECK_MACH, FATAL_ERROR macros.

Dump the task level exception ports.

Add more checks to PAL SXS tests. Validate that the fault addr (ExceptionRecord.ExceptionInformation[1]) is correct.  Check that a third party signal handler will be properly chained to.

Commit migrated from https://github.com/dotnet/coreclr/commit/9f6654b6faec5730f71d6c547ea497e406ae4d47

src/coreclr/src/pal/src/arch/i386/dispatchexceptionwrapper.S
src/coreclr/src/pal/src/exception/machexception.cpp
src/coreclr/src/pal/src/exception/machexception.h
src/coreclr/src/pal/src/exception/machmessage.cpp
src/coreclr/src/pal/src/exception/machmessage.h
src/coreclr/src/pal/src/include/pal/thread.hpp
src/coreclr/src/pal/src/thread/thread.cpp
src/coreclr/src/pal/tests/palsuite/exception_handling/pal_sxs/test1/dlltest1.cpp
src/coreclr/src/pal/tests/palsuite/exception_handling/pal_sxs/test1/dlltest2.cpp
src/coreclr/src/pal/tests/palsuite/exception_handling/pal_sxs/test1/exceptionsxs.cpp

index 46cb6dd..ee5ff46 100644 (file)
 .intel_syntax noprefix
 #include "unixasmmacros.inc"
 
-#if defined(__x86_64__)
-#define PAL_DISPATCHEXCEPTION __Z21PAL_DispatchExceptionmmmmmmP8_CONTEXTP17_EXCEPTION_RECORDP11MachMessage
-#else //!defined(_AMD64_)
-#define PAL_DISPATCHEXCEPTION __Z21PAL_DispatchExceptionP8_CONTEXTP17_EXCEPTION_RECORDP11MachMessage
-#endif // defined(_AMD64_)
-
-// Offset of the return address from the PAL_DISPATCHEXCEPTION in the PAL_DispatchExceptionWrapper
+// Offset of the return address from the PAL_DispatchException in the PAL_DispatchExceptionWrapper
 .globl C_FUNC(PAL_DispatchExceptionReturnOffset)
 C_FUNC(PAL_DispatchExceptionReturnOffset):
-    .int LOCAL_LABEL(PAL_DispatchExceptionReturn)-C_FUNC(PAL_DispatchExceptionWrapper)
+    .int LOCAL_LABEL(PAL_DispatchExceptionReturn) - C_FUNC(PAL_DispatchExceptionWrapper)
 
 //
 // PAL_DispatchExceptionWrapper will never be called; it only serves
@@ -33,14 +27,14 @@ C_FUNC(PAL_DispatchExceptionReturnOffset):
 // unwinding behavior is equivalent to any standard function having
 // an ebp frame. It is analogous to the following source file.
 // 
-// void PAL_DispatchException(CONTEXT *pContext, EXCEPTION_RECORD *pExceptionRecord, MachMessage *pMessage);
+// extern "C" void PAL_DispatchException(CONTEXT *pContext, EXCEPTION_RECORD *pExceptionRecord, MachExceptionInfo *pMachExceptionInfo);
 // 
 // extern "C" void PAL_DispatchExceptionWrapper()
 // {
 //     CONTEXT Context;
 //     EXCEPTION_RECORD ExceptionRecord;
-//     MachMessage Message;
-//     PAL_DispatchException(&Context, &ExceptionRecord, &Message);
+//     MachExceptionInfo MachExceptionInfo;
+//     PAL_DispatchException(&Context, &ExceptionRecord, &MachExceptionInfo);
 // }
 //
 
@@ -49,10 +43,9 @@ NESTED_ENTRY PAL_DispatchExceptionWrapper, _TEXT, NoHandler
     mov     rbp, rsp
     set_cfa_register rbp, (2*8)
     int3
-    call    PAL_DISPATCHEXCEPTION
+    call    C_FUNC(PAL_DispatchException)
 LOCAL_LABEL(PAL_DispatchExceptionReturn):
     int3
     pop_nonvol_reg rbp
     ret
 NESTED_END PAL_DispatchExceptionWrapper, _TEXT
-
index 815186f..ea57173 100644 (file)
@@ -4,8 +4,6 @@
 
 /*++
 
-
-
 Module Name:
 
     machexception.cpp
@@ -14,8 +12,6 @@ Abstract:
 
     Implementation of MACH exception API functions.
 
-
-
 --*/
 
 #include "pal/thread.hpp"
@@ -47,8 +43,6 @@ using namespace CorUnix;
 
 SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
 
-void ForwardMachException(CPalThread *pThread, MachMessage *pMessage);
-
 // The port we use to handle exceptions and to set the thread context
 mach_port_t s_ExceptionPort;
 
@@ -56,6 +50,74 @@ static BOOL s_DebugInitialized = FALSE;
 
 static const char * PAL_MACH_EXCEPTION_MODE = "PAL_MachExceptionMode";
 
+// This struct is used to track the threads that need to have an exception forwarded
+// to the next thread level port in the chain (if exists). An entry is added by the
+// faulting sending a special message to the exception thread which saves it on an
+// list that is searched when the restarted exception notification is received again.
+struct ForwardedException
+{
+    ForwardedException *m_next;
+    thread_act_t Thread;
+    exception_type_t ExceptionType;
+    CPalThread *PalThread;
+};
+
+// The singly linked list and enumerator for the ForwardException struct
+struct ForwardedExceptionList
+{
+private:
+    ForwardedException *m_head;
+    ForwardedException *m_previous;
+
+public:
+    ForwardedException *Current;
+
+    ForwardedExceptionList()
+    {
+        m_head = NULL;
+        MoveFirst();
+    }
+
+    void MoveFirst()
+    {
+        Current = m_head;
+        m_previous = NULL;
+    }
+
+    bool IsEOL()
+    {
+        return Current == NULL;
+    }
+
+    void MoveNext()
+    {
+        m_previous = Current;
+        Current = Current->m_next;
+    }
+
+    void Add(ForwardedException *item)
+    {
+        item->m_next = m_head;
+        m_head = item;
+    }
+
+    void Delete()
+    {
+        if (m_previous == NULL)
+        {
+            m_head = Current->m_next;
+        }
+        else
+        {
+            m_previous->m_next = Current->m_next;
+        }
+        free(Current);
+
+        Current = m_head;
+        m_previous = NULL;
+    }
+};
+
 enum MachExceptionMode
 {
     // special value to indicate we've not initialized yet
@@ -89,7 +151,18 @@ enum MachExceptionMode
     MachException_Default           = 0,
 };
 
-static exception_mask_t GetExceptionMask()
+/*++
+Function :
+    GetExceptionMask()
+
+    Returns the mach exception mask for the exceptions to hook for a thread.
+
+Return value :
+    mach exception mask
+--*/
+static 
+exception_mask_t 
+GetExceptionMask()
 {
     static MachExceptionMode exMode = MachException_Uninitialized;
 
@@ -141,23 +214,6 @@ static exception_mask_t GetExceptionMask()
     return machExceptionMask;
 }
 
-#define NONPAL_RETAIL_ASSERT(...)                                       \
-    {                                                                   \
-        {                                                               \
-            PAL_EnterHolder enterHolder;                                \
-            PAL_printf(__VA_ARGS__);                                    \
-            PAL_DisplayDialogFormatted("NON-PAL ASSERT", __VA_ARGS__);  \
-        }                                                               \
-        DBG_DebugBreak();                                               \
-        abort();                                                        \
-    }
-
-#define CHECK_MACH(function, machret)                                   \
-    if (machret != KERN_SUCCESS)                                        \
-    {                                                                   \
-        NONPAL_RETAIL_ASSERT(function " failed: %08X: %s\n", machret, mach_error_string(machret)); \
-    }
-
 #ifdef FEATURE_PAL_SXS
 
 /*++
@@ -189,142 +245,58 @@ PAL_ERROR CorUnix::CPalThread::EnableMachExceptions()
         countBits = ((countBits & 0xF0F0F0F0) >>  4) + (countBits & 0x0F0F0F0F);
         countBits = ((countBits & 0xFF00FF00) >>  8) + (countBits & 0x00FF00FF);
         countBits = ((countBits & 0xFFFF0000) >> 16) + (countBits & 0x0000FFFF);
-        if (countBits != static_cast<exception_mask_t>(
-            CThreadMachExceptionHandlerNode::s_nPortsMax))
+        if (countBits != static_cast<exception_mask_t>(CThreadMachExceptionHandlers::s_nPortsMax))
         {
             ASSERT("s_nPortsMax is %u, but needs to be %u\n",
-                   CThreadMachExceptionHandlerNode::s_nPortsMax, countBits);
+                   CThreadMachExceptionHandlers::s_nPortsMax, countBits);
         }
 #endif // _DEBUG
 
-        // We store a set of previous handlers and register an exception port that is unique to both (to help
-        // us get the correct chain-back semantics in as many scenarios as possible). The following call tells
-        // us which we should do.
-        CThreadMachExceptionHandlerNode *pSavedHandlers = m_sMachExceptionHandlers.GetNodeForInitialization();
-        NONPAL_TRACE("Enabling handlers for thread %08X exception port %08X\n", GetMachPortSelf(), s_ExceptionPort);
+        NONPAL_TRACE("Enabling handlers for thread %08x exception mask %08x exception port %08x\n", 
+            GetMachPortSelf(), machExceptionMask, s_ExceptionPort);
+
+        CThreadMachExceptionHandlers *pSavedHandlers = GetSavedMachHandlers();
 
         // Swap current handlers into temporary storage first. That's because it's possible (even likely) that
         // some or all of the handlers might still be ours. In those cases we don't want to overwrite the
         // chain-back entries with these useless self-references.
-        kern_return_t MachRet;
-        mach_msg_type_number_t oldCount = CThreadMachExceptionHandlerNode::s_nPortsMax;
-        exception_mask_t rgMasks[CThreadMachExceptionHandlerNode::s_nPortsMax];
-        exception_handler_t rgHandlers[CThreadMachExceptionHandlerNode::s_nPortsMax];
-        exception_behavior_t rgBehaviors[CThreadMachExceptionHandlerNode::s_nPortsMax];
-        thread_state_flavor_t rgFlavors[CThreadMachExceptionHandlerNode::s_nPortsMax];
+        kern_return_t machret;
+        kern_return_t machretDeallocate;
         thread_port_t thread = mach_thread_self();
-        exception_behavior_t excepBehavior = EXCEPTION_STATE_IDENTITY;
-
-        MachRet = thread_swap_exception_ports(thread,
-                                              machExceptionMask,
-                                              s_ExceptionPort,
-                                              excepBehavior,
-                                              MACHINE_THREAD_STATE,
-                                              rgMasks,
-                                              &oldCount,
-                                              rgHandlers,
-                                              rgBehaviors,
-                                              rgFlavors);
-
-        kern_return_t MachRetDeallocate = mach_port_deallocate(mach_task_self(), thread);
-        CHECK_MACH("mach_port_deallocate", MachRetDeallocate);
 
-        if (MachRet != KERN_SUCCESS)
+        machret = thread_swap_exception_ports(
+            thread,
+            machExceptionMask,
+            s_ExceptionPort,
+            EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
+            THREAD_STATE_NONE,
+            pSavedHandlers->m_masks,
+            &pSavedHandlers->m_nPorts,
+            pSavedHandlers->m_handlers,
+            pSavedHandlers->m_behaviors,
+            pSavedHandlers->m_flavors);
+
+        machretDeallocate = mach_port_deallocate(mach_task_self(), thread);
+        CHECK_MACH("mach_port_deallocate", machretDeallocate);
+
+        if (machret != KERN_SUCCESS)
         {
-            ASSERT("thread_swap_exception_ports failed: %d\n", MachRet);
-            return UTIL_MachErrorToPalError(MachRet);
+            ASSERT("thread_swap_exception_ports failed: %d %s\n", machret, mach_error_string(machret));
+            return UTIL_MachErrorToPalError(machret);
         }
 
-        // Scan through the returned handlers looking for those that are ours.
-        for (mach_msg_type_number_t i = 0; i < oldCount; i++)
+#ifdef _DEBUG
+        NONPAL_TRACE("EnableMachExceptions: THREAD PORT count %d\n", pSavedHandlers->m_nPorts);
+        for (mach_msg_type_number_t i = 0; i < pSavedHandlers->m_nPorts; i++)
         {
-            if (rgHandlers[i] == s_ExceptionPort)
-            {
-                // We were already registered for the exceptions indicated by rgMasks[i]. Look through each
-                // exception (set bit in the mask) separately, checking whether we previously had a (non-CLR)
-                // registration for that handle.
-                for (size_t j = 0; j < (sizeof(exception_mask_t) * 8); j++)
-                {
-                    // Skip unset bits (exceptions not covered by this entry).
-                    exception_mask_t bmException = rgMasks[i] & (1 << j);
-                    if (bmException == 0)
-                        continue;
-
-                    // Find record in the previous data that covers this exception.
-                    bool fFoundPreviousHandler = false;
-                    for (int k = 0; k < pSavedHandlers->m_nPorts; k++)
-                    {
-                        // Skip records for different exceptions.
-                        if (!(pSavedHandlers->m_masks[k] & bmException))
-                            continue;
-
-                        // Found one. By definition it shouldn't be one of our handlers.
-                        if (pSavedHandlers->m_handlers[k] == s_ExceptionPort)
-                            ASSERT("Stored our own handlers in Mach exception chain-back info.\n");
-
-                        // We need to replicate the handling details back into our temporary data in place of
-                        // the CLR record. There are several things that can happen:
-                        // 1) One of the other entries has the same handler, behavior and flavor (for a
-                        //    different set of exceptions). We could merge the data for this exception into
-                        //    that record (set another bit in the masks array entry).
-                        // 2) This was the only exception in the current entry (only one bit was set in the
-                        //    mask) and we can simply re-use this entry (overwrite the handler, behavior and
-                        //    flavor entries).
-                        // 3) Multiple exceptions were covered by this entry. In this case we should add a new
-                        //    entry covering just the current exception. We're guaranteed to have space to do
-                        //    this since we allocated enough entries to cover one exception per-entry and we
-                        //    have at least one entry with two or more exceptions (this one).
-                        // It turns out we can ignore case 1 (which involves complicating our logic still
-                        // further) since we have no requirement to tightly pack all the entries for the same
-                        // handler/behavior/flavor (like thread_swap_exception_ports does). We're perfectly
-                        // happy having six entries for six exceptions handled by identical handlers rather
-                        // than a single entry with six bits set in the exception mask.
-                        if (rgMasks[i] == bmException)
-                        {
-                            // Entry was only for this exception. Simply overwrite handler/behavior and flavor
-                            // with the stored values.
-                            rgHandlers[i] = pSavedHandlers->m_handlers[k];
-                            rgBehaviors[i] = pSavedHandlers->m_behaviors[k];
-                            rgFlavors[i] = pSavedHandlers->m_flavors[k];
-                        }
-                        else
-                        {
-                            // More than one exception handled by this record. Store the old data in a new
-                            // cell of the temporary data and remove the exception from the old cell.
-                            if ((int)oldCount == CThreadMachExceptionHandlerNode::s_nPortsMax)
-                                ASSERT("Ran out of space to expand exception handlers. This shouldn't happen.\n");
-
-                            rgMasks[oldCount] = bmException;
-                            rgHandlers[oldCount] = pSavedHandlers->m_handlers[k];
-                            rgBehaviors[oldCount] = pSavedHandlers->m_behaviors[k];
-                            rgFlavors[oldCount] = pSavedHandlers->m_flavors[k];
-
-                            // The old cell no longer describes this exception.
-                            rgMasks[i] &= ~bmException;
-
-                            oldCount++;
-                        }
-
-                        // We found a match.
-                        fFoundPreviousHandler = true;
-                        break;
-                    }
-
-                    // If we didn't find a match then we still don't want to record our own handler. Just
-                    // reset the bit in the masks value (implicitly recording that we have no-chain back entry
-                    // for this exception).
-                    if (!fFoundPreviousHandler)
-                        rgMasks[i] &= ~bmException;
-                }
-            }
+            _ASSERTE(pSavedHandlers->m_handlers[i] != s_ExceptionPort);
+            NONPAL_TRACE("EnableMachExceptions: THREAD PORT mask %08x handler: %08x behavior %08x flavor %u\n",
+                pSavedHandlers->m_masks[i],
+                pSavedHandlers->m_handlers[i],
+                pSavedHandlers->m_behaviors[i],
+                pSavedHandlers->m_flavors[i]);
         }
-
-        // We've cleaned any mention of our own handlers from the data. It's safe to persist it.
-        pSavedHandlers->m_nPorts = oldCount;
-        memcpy(pSavedHandlers->m_masks, rgMasks, sizeof(rgMasks));
-        memcpy(pSavedHandlers->m_handlers, rgHandlers, sizeof(rgHandlers));
-        memcpy(pSavedHandlers->m_behaviors, rgBehaviors, sizeof(rgBehaviors));
-        memcpy(pSavedHandlers->m_flavors, rgFlavors, sizeof(rgFlavors));
+#endif // _DEBUG
     }
     return ERROR_SUCCESS;
 }
@@ -352,14 +324,8 @@ PAL_ERROR CorUnix::CPalThread::DisableMachExceptions()
     if (0 == GetExceptionMask())
         return palError;
     
-    // Get the handlers to restore. It isn't really as simple as this. We keep two sets of handlers (which
-    // improves our ability to chain correctly in more scenarios) but this means we can encounter dilemmas
-    // where we've recorded two different handlers for the same port and can only re-register one of them
-    // (with a very high chance that it does not chain to the other). I don't believe it matters much today:
-    // in the absence of CoreCLR shutdown we don't throw away our thread context until a thread dies (in fact
-    // usually a bit later than this). Hopefully by the time this changes we'll have a better design for
-    // hardware exception handling overall.
-    CThreadMachExceptionHandlerNode *savedPorts = m_sMachExceptionHandlers.GetNodeForCleanup();
+    // Get the handlers to restore.
+    CThreadMachExceptionHandlers *savedPorts = GetSavedMachHandlers();
 
     kern_return_t MachRet = KERN_SUCCESS;
     for (int i = 0; i < savedPorts->m_nPorts; i++)
@@ -368,16 +334,15 @@ PAL_ERROR CorUnix::CPalThread::DisableMachExceptions()
         // MACH_PORT_NULL for the handler and zero values for behavior
         // and flavor.  Unfortunately, the latter are invalid even for
         // MACH_PORT_NULL when you use thread_set_exception_ports.
-        exception_behavior_t behavior =
-            savedPorts->m_behaviors[i] ? savedPorts->m_behaviors[i] : EXCEPTION_DEFAULT;
-        thread_state_flavor_t flavor =
-            savedPorts->m_flavors[i] ? savedPorts->m_flavors[i] : MACHINE_THREAD_STATE;
+        exception_behavior_t behavior = savedPorts->m_behaviors[i] ? savedPorts->m_behaviors[i] : EXCEPTION_DEFAULT;
+        thread_state_flavor_t flavor = savedPorts->m_flavors[i] ? savedPorts->m_flavors[i] : MACHINE_THREAD_STATE;
         thread_port_t thread = mach_thread_self();
         MachRet = thread_set_exception_ports(thread,
                                              savedPorts->m_masks[i],
                                              savedPorts->m_handlers[i],
                                              behavior,
                                              flavor);
+
         kern_return_t MachRetDeallocate = mach_port_deallocate(mach_task_self(), thread);
         CHECK_MACH("mach_port_deallocate", MachRetDeallocate);
                                              
@@ -467,14 +432,15 @@ BOOL SEHDisableMachExceptions()
 #endif // FEATURE_PAL_SXS
 
 #if !defined(_AMD64_)
-void PAL_DispatchException(PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachMessage *pMessage)
+extern "C"
+void PAL_DispatchException(PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
 #else // defined(_AMD64_)
 
-// Since HijackFaultingThread pushed the context, exception record and mach exception message on the stack,
-// we need to adjust the signature of PAL_DispatchException such that the corresponding arguments are considered
-// to be on the stack per GCC64 calling convention rules. Hence, the first 6 dummy arguments (corresponding to RDI,
-// RSI, RDX,RCX, R8, R9).
-void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64 dwRCX, DWORD64 dwR8, DWORD64 dwR9, PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachMessage *pMessage)
+// Since HijackFaultingThread pushed the context, exception record and info on the stack, we need to adjust the 
+// signature of PAL_DispatchException such that the corresponding arguments are considered to be on the stack 
+// per GCC64 calling convention rules. Hence, the first 6 dummy arguments (corresponding to RDI, RSI, RDX,RCX, R8, R9).
+extern "C"
+void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64 dwRCX, DWORD64 dwR8, DWORD64 dwR9, PCONTEXT pContext, PEXCEPTION_RECORD pExRecord, MachExceptionInfo *pMachExceptionInfo)
 #endif // !defined(_AMD64_)
 {
     CPalThread *pThread = InternalGetCurrentThread();
@@ -496,8 +462,15 @@ void PAL_DispatchException(DWORD64 dwRDI, DWORD64 dwRSI, DWORD64 dwRDX, DWORD64
     TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
     SEHProcessException(&pointers);
 
-    // Chain the exception to the next PAL
-    ForwardMachException(pThread, pMessage);
+    // Send the forward request to the exception thread to process
+    MachMessage sSendMessage;
+    sSendMessage.SendForwardException(s_ExceptionPort, pMachExceptionInfo, pThread);
+
+    // Spin wait until this thread is hijacked by the exception thread
+    while (TRUE)
+    {
+        sched_yield();
+    }
 }
 
 #if defined(_X86_) || defined(_AMD64_)
@@ -507,39 +480,29 @@ extern "C" int PAL_DispatchExceptionReturnOffset;
 
 /*++
 Function :
-    ExceptionRecordFromMessage
+    BuildExceptionRecord
 
-    Setups up an ExceptionRecord from an exception message
+    Sets up up an ExceptionRecord from an exception message
 
 Parameters :
-    message - exception message to build the exception record
+    exceptionInfo - exception info to build the exception record
     pExceptionRecord - exception record to setup
 */
-static void 
-ExceptionRecordFromMessage(
-    MachMessage &message,               // [in] exception message
-    EXCEPTION_RECORD *pExceptionRecord) // [out] Used to return exception parameters
+static 
+void 
+BuildExceptionRecord(
+    MachExceptionInfo& exceptionInfo,               // [in] exception info
+    EXCEPTION_RECORD *pExceptionRecord)             // [out] Used to return exception parameters
 {
-    exception_type_t exception = message.GetException();
-    MACH_EH_TYPE(exception_data_type_t) subcodes[2];
-    mach_msg_type_number_t subcode_count;
-    
-    subcode_count = message.GetExceptionCodeCount();
-    if (subcode_count < 0 || subcode_count > 2)
-        NONPAL_RETAIL_ASSERT("Bad exception subcode count: %d", subcode_count);
-
-    for (int i = 0; i < subcode_count; i++)
-        subcodes[i] = message.GetExceptionCode(i);
-
     memset(pExceptionRecord, 0, sizeof(EXCEPTION_RECORD));
 
     DWORD exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
 
-    switch(exception)
+    switch(exceptionInfo.ExceptionType)
     {
     // Could not access memory. subcode contains the bad memory address. 
     case EXC_BAD_ACCESS:
-        if (subcode_count != 2)
+        if (exceptionInfo.SubcodeCount != 2)
         {
             NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
             exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
@@ -550,8 +513,8 @@ ExceptionRecordFromMessage(
 
             pExceptionRecord->NumberParameters = 2;
             pExceptionRecord->ExceptionInformation[0] = 0;
-            pExceptionRecord->ExceptionInformation[1] = subcodes[1];
-            NONPAL_TRACE("subcodes[1] = %llx\n", subcodes[1]);
+            pExceptionRecord->ExceptionInformation[1] = exceptionInfo.Subcodes[1];
+            NONPAL_TRACE("subcodes[1] = %llx\n", exceptionInfo.Subcodes[1]);
         }
         break;
 
@@ -564,14 +527,14 @@ ExceptionRecordFromMessage(
 
     // Arithmetic exception; exact nature of exception is in subcode field. 
     case EXC_ARITHMETIC:
-        if (subcode_count != 2)
+        if (exceptionInfo.SubcodeCount != 2)
         {
             NONPAL_RETAIL_ASSERT("Got an unexpected subcode");
             exceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; 
         }
         else
         {
-            switch (subcodes[0])
+            switch (exceptionInfo.Subcodes[0])
             {
 #if defined(_X86_) || defined(_AMD64_)
                 case EXC_I386_DIV:
@@ -607,11 +570,11 @@ ExceptionRecordFromMessage(
     // Trace, breakpoint, etc. Details in subcode field. 
     case EXC_BREAKPOINT:
 #if defined(_X86_) || defined(_AMD64_)
-        if (subcodes[0] == EXC_I386_SGL)
+        if (exceptionInfo.Subcodes[0] == EXC_I386_SGL)
         {
             exceptionCode = EXCEPTION_SINGLE_STEP;
         }
-        else if (subcodes[0] == EXC_I386_BPT)
+        else if (exceptionInfo.Subcodes[0] == EXC_I386_BPT)
         {
             exceptionCode = EXCEPTION_BREAKPOINT;
         }
@@ -620,7 +583,7 @@ ExceptionRecordFromMessage(
 #endif
         else
         {
-            WARN("unexpected subcode %d for EXC_BREAKPOINT", subcodes[0]);
+            WARN("unexpected subcode %d for EXC_BREAKPOINT", exceptionInfo.Subcodes[0]);
             exceptionCode = EXCEPTION_BREAKPOINT;
         }
         break;
@@ -637,7 +600,7 @@ ExceptionRecordFromMessage(
         break;
 
     default:
-        ASSERT("Got unknown trap code %d\n", exception);
+        NONPAL_ASSERT("Got unknown trap code %d\n", exceptionInfo.ExceptionType);
         break;
     }
 
@@ -674,7 +637,7 @@ GetExceptionString(
         return "EXC_MACH_SYSCALL";
 
     default:
-        ASSERT("Got unknown trap code %d\n", exception);
+        NONPAL_ASSERT("Got unknown trap code %d\n", exception);
         break;
     }
     return "INVALID CODE";
@@ -686,7 +649,7 @@ Function :
     HijackFaultingThread
 
     Sets the faulting thread up to return to PAL_DispatchException with an
-    ExceptionRecord, thread CONTEXT and the exception MachMessage.
+    ExceptionRecord and thread CONTEXT.
 
 Parameters:
     thread - thread the exception happened
@@ -696,43 +659,30 @@ Parameters:
 Return value :
     None
 --*/
-
 static
 void
 HijackFaultingThread(
     mach_port_t thread,             // [in] thread the exception happened on
     mach_port_t task,               // [in] task the exception happened on
-    MachMessage &message)           // [in] exception message
+    MachMessagemessage)           // [in] exception message
 {
-    thread_state_flavor_t threadStateFlavor;
-    x86_thread_state_t threadState;
+    MachExceptionInfo exceptionInfo(thread, message);
     EXCEPTION_RECORD exceptionRecord;
     CONTEXT threadContext;
     kern_return_t machret;
-    unsigned int count;
 
-    // Fill in the exception record from the exception message
-    ExceptionRecordFromMessage(message, &exceptionRecord);
+    // Fill in the exception record from the exception info
+    BuildExceptionRecord(exceptionInfo, &exceptionRecord);
     
-    // Get the thread state from the exception message and convert the count of bytes into
-    // the count of ints
-    threadStateFlavor = message.GetThreadStateFlavor();
-    count = message.GetThreadState(threadStateFlavor, (thread_state_t)&threadState, thread) / sizeof(natural_t);
-
 #ifdef _X86_
-    threadContext.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS
+    threadContext.ContextFlags = CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
 #else
     threadContext.ContextFlags = CONTEXT_FLOATING_POINT;
 #endif
-    // Get just the floating point registers directly from the thread because the message context is only
-    // the general registers.
-    machret = CONTEXT_GetThreadContextFromPort(thread, &threadContext);
-    CHECK_MACH("CONTEXT_GetThreadContextFromPort", machret);
+    CONTEXT_GetThreadContextFromThreadState(x86_FLOAT_STATE, (thread_state_t)&exceptionInfo.FloatState, &threadContext);
 
-    // Now get the rest of the registers from the exception message. Don't save/restore the debug registers 
-    // because loading them on OSx causes a privileged instruction fault. The "DE" in CR4 is set.
     threadContext.ContextFlags |= CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
-    CONTEXT_GetThreadContextFromThreadState(threadStateFlavor, (thread_state_t)&threadState, &threadContext);
+    CONTEXT_GetThreadContextFromThreadState(x86_THREAD_STATE, (thread_state_t)&exceptionInfo.ThreadState, &threadContext);
 
 #if defined(CORECLR) && (defined(_X86_) || defined(_AMD64_))
     // For CoreCLR we look more deeply at access violations to determine whether they're the result of a stack
@@ -826,13 +776,13 @@ HijackFaultingThread(
 #else
             machret = vm_region(
 #endif
-                                mach_task_self(),
-                                &vm_address,
-                                &vm_size,
-                                vm_flavor,
-                                (vm_region_info_t)&info,
-                                &infoCnt,
-                                &object_name);
+                mach_task_self(),
+                &vm_address,
+                &vm_size,
+                vm_flavor,
+                (vm_region_info_t)&info,
+                &infoCnt,
+                &object_name);
 #ifdef _X86_
             CHECK_MACH("vm_region", machret);
 #elif defined(_AMD64_)
@@ -852,7 +802,7 @@ HijackFaultingThread(
         {
             // Check if we can read pointer sizeD bytes below the target thread's stack pointer.
             // If we are unable to, then it implies we have run into SO.
-            void **targetSP = (void **)threadState.uts.ts64.__rsp;
+            void **targetSP = (void **)threadContext.Rsp;
             vm_address_t targetAddr = (mach_vm_address_t)(targetSP);
             targetAddr -= sizeof(void *);
             vm_size_t vm_size = sizeof(void *);
@@ -890,21 +840,25 @@ HijackFaultingThread(
 #endif // CORECLR && _X86_
 
 #if defined(_X86_)
-    _ASSERTE((threadStateFlavor == x86_THREAD_STATE32) || ((threadStateFlavor == x86_THREAD_STATE) && (threadState.tsh.flavor == x86_THREAD_STATE32)));
+    NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE32);
+
+    // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
+    // the state if the exception is forwarded.
+    x86_thread_state32_t ts32 = exceptionInfo.ThreadState.uts.ts32;
 
     // If we're in single step mode, disable it since we're going to call PAL_DispatchException
     if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
     {
-        threadState.uts.ts32.eflags &= ~EFL_TF;
+        ts32.eflags &= ~EFL_TF;
     }
 
     exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL; 
     exceptionRecord.ExceptionRecord = NULL;
-    exceptionRecord.ExceptionAddress = (void *)threadContext.Eip;
+    exceptionRecord.ExceptionAddress = (void *)ts32.eip;
 
-    void **FramePointer = (void **)threadState.uts.ts32.esp;
+    void **FramePointer = (void **)ts32.esp;
 
-    *--FramePointer = (void *)((ULONG_PTR)threadState.uts.ts32.eip);
+    *--FramePointer = (void *)ts32.eip;
 
     // Construct a stack frame for a pretend activation of the function
     // PAL_DispatchExceptionWrapper that serves only to make the stack
@@ -912,8 +866,8 @@ HijackFaultingThread(
     // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
     // are the context and exception record, and it has just "called"
     // PAL_DispatchException.
-    *--FramePointer = (void *)threadState.uts.ts32.ebp;
-    threadState.uts.ts32.ebp = (unsigned)FramePointer;
+    *--FramePointer = (void *)ts32.ebp;
+    ts32.ebp = (unsigned)FramePointer;
 
     // Put the context on the stack
     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
@@ -927,10 +881,9 @@ HijackFaultingThread(
     EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
     *pExceptionRecord = exceptionRecord;
 
-    // Put the exception message on the stack
-    FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachMessage));
-    MachMessage *pMessage = (MachMessage *)FramePointer;
-    pMessage->InitializeFrom(message);
+    FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
+    MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
+    *pMachExceptionInfo = exceptionInfo;
 
     // Push arguments to PAL_DispatchException
     FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
@@ -939,30 +892,38 @@ HijackFaultingThread(
     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
     FramePointer[0] = pContext;
     FramePointer[1] = pExceptionRecord;
-    FramePointer[2] = pMessage;
+    FramePointer[2] = pMachExceptionInfo;
 
     // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
     FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
 
     // Make the instruction register point to DispatchException
-    threadState.uts.ts32.eip = (unsigned)PAL_DispatchException;
-    threadState.uts.ts32.esp = (unsigned)&FramePointer[-1]; // skip return address
+    ts32.eip = (unsigned)PAL_DispatchException;
+    ts32.esp = (unsigned)&FramePointer[-1]; // skip return address
+
+    // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
+    machret = thread_set_state(thread, x86_THREAD_STATE32, (thread_state_t)&ts32, x86_THREAD_STATE32_COUNT);
+    CHECK_MACH("thread_set_state(thread)", machret);
 #elif defined(_AMD64_)
-    _ASSERTE((threadStateFlavor == x86_THREAD_STATE64) || ((threadStateFlavor == x86_THREAD_STATE) && (threadState.tsh.flavor == x86_THREAD_STATE64)));
+    NONPAL_ASSERTE(exceptionInfo.ThreadState.tsh.flavor == x86_THREAD_STATE64);
+
+    // Make a copy of the thread state because the one in exceptionInfo needs to be preserved to restore
+    // the state if the exception is forwarded.
+    x86_thread_state64_t ts64 = exceptionInfo.ThreadState.uts.ts64;
 
     // If we're in single step mode, disable it since we're going to call PAL_DispatchException
     if (exceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
     {
-        threadState.uts.ts64.__rflags &= ~EFL_TF;
+        ts64.__rflags &= ~EFL_TF;
     }
 
     exceptionRecord.ExceptionFlags = EXCEPTION_IS_SIGNAL; 
     exceptionRecord.ExceptionRecord = NULL;
-    exceptionRecord.ExceptionAddress = (void *)threadContext.Rip;
+    exceptionRecord.ExceptionAddress = (void *)ts64.__rip;
 
-    void **FramePointer = (void **)threadState.uts.ts64.__rsp;
+    void **FramePointer = (void **)ts64.__rsp;
 
-    *--FramePointer = (void *)((ULONG_PTR)threadState.uts.ts64.__rip);
+    *--FramePointer = (void *)ts64.__rip;
 
     // Construct a stack frame for a pretend activation of the function
     // PAL_DispatchExceptionWrapper that serves only to make the stack
@@ -970,8 +931,8 @@ HijackFaultingThread(
     // PAL_DispatchExceptionWrapper has an ebp frame, its local variables
     // are the context and exception record, and it has just "called"
     // PAL_DispatchException.
-    *--FramePointer = (void *)threadState.uts.ts64.__rbp;
-    threadState.uts.ts64.__rbp = (SIZE_T)FramePointer;
+    *--FramePointer = (void *)ts64.__rbp;
+    ts64.__rbp = (SIZE_T)FramePointer;
 
     // Put the context on the stack
     FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(CONTEXT));
@@ -985,10 +946,9 @@ HijackFaultingThread(
     EXCEPTION_RECORD *pExceptionRecord = (EXCEPTION_RECORD *)FramePointer;
     *pExceptionRecord = exceptionRecord;
 
-    // Put the exception message on the stack
-    FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachMessage));
-    MachMessage *pMessage = (MachMessage *)FramePointer;
-    pMessage->InitializeFrom(message);
+    FramePointer = (void **)((ULONG_PTR)FramePointer - sizeof(MachExceptionInfo));
+    MachExceptionInfo *pMachExceptionInfo = (MachExceptionInfo *)FramePointer;
+    *pMachExceptionInfo = exceptionInfo;
 
     // Push arguments to PAL_DispatchException
     FramePointer = (void **)((ULONG_PTR)FramePointer - 3 * sizeof(void *));
@@ -997,21 +957,59 @@ HijackFaultingThread(
     FramePointer = (void **)((ULONG_PTR)FramePointer - ((ULONG_PTR)FramePointer % 16));
     FramePointer[0] = pContext;
     FramePointer[1] = pExceptionRecord;
-    FramePointer[2] = pMessage;
+    FramePointer[2] = pMachExceptionInfo;
 
     // Place the return address to right after the fake call in PAL_DispatchExceptionWrapper
     FramePointer[-1] = (void *)((ULONG_PTR)PAL_DispatchExceptionWrapper + PAL_DispatchExceptionReturnOffset);
 
     // Make the instruction register point to DispatchException
-    threadState.uts.ts64.__rip = (SIZE_T)PAL_DispatchException;
-    threadState.uts.ts64.__rsp = (SIZE_T)&FramePointer[-1]; // skip return address
+    ts64.__rip = (SIZE_T)PAL_DispatchException;
+    ts64.__rsp = (SIZE_T)&FramePointer[-1]; // skip return address
+
+    // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
+    machret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)&ts64, x86_THREAD_STATE64_COUNT);
+    CHECK_MACH("thread_set_state(thread)", machret);
 #else
 #error HijackFaultingThread not defined for this architecture
 #endif
+}
 
-    // Now set the thread state for the faulting thread so that PAL_DispatchException executes next
-    machret = thread_set_state(thread, threadStateFlavor, (thread_state_t)&threadState, count);
-    CHECK_MACH("thread_set_state", machret);
+/*++
+Function :
+    SuspendMachThread
+
+    Suspend the specified thread.
+
+Parameters:
+    thread - mach thread port
+
+Return value :
+    None
+--*/
+static
+void
+SuspendMachThread(thread_act_t thread)
+{
+    kern_return_t machret;
+
+    while (true)
+    {
+        machret = thread_suspend(thread);
+        CHECK_MACH("thread_suspend", machret);
+
+        // Ensure that if the thread was running in the kernel, the kernel operation
+        // is safely aborted so that it can be restarted later.
+        machret = thread_abort_safely(thread);
+        if (machret == KERN_SUCCESS)
+        {
+            break;
+        }
+
+        // The thread was running in the kernel executing a non-atomic operation
+        // that cannot be restarted, so we need to resume the thread and retry
+        machret = thread_resume(thread);
+        CHECK_MACH("thread_resume", machret);
+    }
 }
 
 /*++
@@ -1044,13 +1042,14 @@ Parameters :
 Return value :
    Never returns
 --*/
-void *SEHExceptionThread(void *args)
+void *
+SEHExceptionThread(void *args)
 {
-    MachMessage sMessage;
+    ForwardedExceptionList feList;
     MachMessage sReplyOrForward;
-
+    MachMessage sMessage;
     kern_return_t machret;
-    thread_act_t hThread;
+    thread_act_t thread;
 
     // Loop processing incoming messages forever.
     while (true)
@@ -1058,8 +1057,9 @@ void *SEHExceptionThread(void *args)
         // Receive the next message.
         sMessage.Receive(s_ExceptionPort);
 
-        NONPAL_TRACE("Received message %s from %08x to %08x\n",
+        NONPAL_TRACE("Received message %s (%08x) from (remote) %08x to (local) %08x\n",
             sMessage.GetMessageTypeName(), 
+            sMessage.GetMessageType(),
             sMessage.GetRemotePort(), 
             sMessage.GetLocalPort());
 
@@ -1067,52 +1067,149 @@ void *SEHExceptionThread(void *args)
         {
             // Handle a request to set the thread context for the specified target thread.
             CONTEXT sContext;
-            hThread = sMessage.GetThreadContext(&sContext);
+            thread = sMessage.GetThreadContext(&sContext);
 
-            while (true)
-            {
-                machret = thread_suspend(hThread);
-                CHECK_MACH("thread_suspend", machret);
-
-                // Ensure that if the thread was running in the kernel, the kernel operation
-                // is safely aborted so that it can be restarted later.
-                machret = thread_abort_safely(hThread);
-                if (machret == KERN_SUCCESS)
-                {
-                    break;
-                }
-
-                // The thread was running in the kernel executing a non-atomic operation
-                // that cannot be restarted, so we need to resume the thread and retry
-                machret = thread_resume(hThread);
-                CHECK_MACH("thread_resume", machret);
-            }
+            // Suspend the target thread
+            SuspendMachThread(thread);
             
-            machret = CONTEXT_SetThreadContextOnPort(hThread, &sContext);
+            machret = CONTEXT_SetThreadContextOnPort(thread, &sContext);
             CHECK_MACH("CONTEXT_SetThreadContextOnPort", machret);
 
-            machret = thread_resume(hThread);
+            machret = thread_resume(thread);
             CHECK_MACH("thread_resume", machret);
         }
         else if (sMessage.IsExceptionNotification())
         {
             // This is a notification of an exception occurring on another thread.
-            hThread = sMessage.GetThread();
+            exception_type_t exceptionType = sMessage.GetException();
+            thread = sMessage.GetThread();
 
-            NONPAL_TRACE("Notification is for exception %u (%s) on thread port %08x flavor %d\n",
-                sMessage.GetException(),
-                GetExceptionString(sMessage.GetException()),
-                hThread,
-                sMessage.GetThreadStateFlavor());
+#ifdef _DEBUG 
+            if (NONPAL_TRACE_ENABLED)
+            {
+                NONPAL_TRACE("ExceptionNotification %s (%u) thread %08x flavor %u\n",
+                    GetExceptionString(exceptionType),
+                    exceptionType,
+                    thread,
+                    sMessage.GetThreadStateFlavor());
+
+                int subcode_count = sMessage.GetExceptionCodeCount();
+                for (int i = 0; i < subcode_count; i++)
+                    NONPAL_TRACE("ExceptionNotification subcode[%d] = %llx\n", i, sMessage.GetExceptionCode(i));
+
+                x86_thread_state64_t threadStateActual;
+                unsigned int count = sizeof(threadStateActual) / sizeof(unsigned);
+                machret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&threadStateActual, &count);
+                CHECK_MACH("thread_get_state", machret);
+
+                NONPAL_TRACE("ExceptionNotification actual  rip %016llx rsp %016llx rbp %016llx rax %016llx r15 %016llx eflags %08llx\n",
+                    threadStateActual.__rip,
+                    threadStateActual.__rsp,
+                    threadStateActual.__rbp,
+                    threadStateActual.__rax,
+                    threadStateActual.__r15,
+                    threadStateActual.__rflags);
+
+                x86_exception_state64_t threadExceptionState;
+                unsigned int ehStateCount = sizeof(threadExceptionState) / sizeof(unsigned);
+                machret = thread_get_state(thread, x86_EXCEPTION_STATE64, (thread_state_t)&threadExceptionState, &ehStateCount);
+                CHECK_MACH("thread_get_state", machret);
+
+                NONPAL_TRACE("ExceptionNotification trapno %04x cpu %04x err %08x faultAddr %016llx\n",
+                    threadExceptionState.__trapno,
+                    threadExceptionState.__cpu,
+                    threadExceptionState.__err,
+                    threadExceptionState.__faultvaddr);
+            }
+#endif // _DEBUG
 
-            HijackFaultingThread(hThread, mach_task_self(), sMessage);
+            bool feFound = false;
+            feList.MoveFirst();
 
-            // Send the result of handling the exception back in a reply.
-            sReplyOrForward.ReplyToNotification(&sMessage, KERN_SUCCESS);
+            while (!feList.IsEOL())
+            {
+                mach_port_type_t ePortType;
+                if (mach_port_type(mach_task_self(), feList.Current->Thread, &ePortType) != KERN_SUCCESS || (ePortType & MACH_PORT_TYPE_DEAD_NAME))
+                {
+                    NONPAL_TRACE("Forwarded exception: invalid thread port %08x\n", feList.Current->Thread);
+
+                    // Unlink and delete the forwarded exception instance
+                    feList.Delete();
+                }
+                else
+                {
+                    if (feList.Current->Thread == thread)
+                    {
+                        bool isSameException = feList.Current->ExceptionType == exceptionType;
+                        feFound = true;
+
+                        // Locate the record of previously installed handlers that the target thread keeps.
+                        CThreadMachExceptionHandlers *pHandlers = feList.Current->PalThread->GetSavedMachHandlers();
+
+                        // Unlink and delete the forwarded exception instance
+                        feList.Delete();
+
+                        // Check if the current exception type matches the forwarded one and whether 
+                        // there's a handler for the particular exception we've been handed.
+                        MachExceptionHandler sHandler;
+                        if (isSameException && pHandlers->GetHandler(exceptionType, &sHandler))
+                        {
+                            NONPAL_TRACE("ForwardNotification thread %08x to handler %08x\n", thread, sHandler.m_handler);
+                            sReplyOrForward.ForwardNotification(&sHandler, sMessage);
+                        }
+                        else
+                        {
+                            NONPAL_TRACE("ReplyToNotification KERN_FAILURE thread %08x port %08x sameException %d\n", 
+                                thread, sMessage.GetRemotePort(), isSameException);
+                            sReplyOrForward.ReplyToNotification(sMessage, KERN_FAILURE);
+                        }
+                        break;
+                    }
+
+                    feList.MoveNext();
+                }
+            }
+
+            if (!feFound)
+            {
+                NONPAL_TRACE("HijackFaultingThread thread %08x\n", thread);
+                HijackFaultingThread(thread, mach_task_self(), sMessage);
+
+                // Send the result of handling the exception back in a reply.
+                NONPAL_TRACE("ReplyToNotification KERN_SUCCESS thread %08x port %08x\n", thread, sMessage.GetRemotePort());
+                sReplyOrForward.ReplyToNotification(sMessage, KERN_SUCCESS);
+            }
         }
-        else if (sMessage.IsExceptionReply())
+        else if (sMessage.IsForwardExceptionRequest())
         {
-            NONPAL_TRACE("Exception reply - ignored\n");
+            thread = sMessage.GetThread();
+
+            NONPAL_TRACE("ForwardExceptionRequest for thread %08x\n", thread);
+
+            // Suspend the faulting thread. 
+            SuspendMachThread(thread);
+
+            // Set the context back to the original faulting state.
+            MachExceptionInfo *pExceptionInfo = sMessage.GetExceptionInfo();
+            pExceptionInfo->RestoreState(thread);
+
+            // Allocate an forwarded exception entry
+            ForwardedException *pfe = (ForwardedException *)malloc(sizeof(ForwardedException));
+            if (pfe == NULL)
+            {
+                NONPAL_RETAIL_ASSERT("Exception thread ran out of memory to track forwarded exception notifications");
+            }
+
+            // Save the forwarded exception entry away for the restarted exception message
+            pfe->Thread = thread;
+            pfe->ExceptionType = pExceptionInfo->ExceptionType;
+            pfe->PalThread = sMessage.GetPalThread();
+            feList.Add(pfe);
+
+            // Now let the thread run at the original exception context to restart the exception
+            NONPAL_TRACE("ForwardExceptionRequest resuming thread %08x exception type %08x\n", thread, pfe->ExceptionType);
+            machret = thread_resume(thread);
+            CHECK_MACH("thread_resume", machret);
         }
         else
         {
@@ -1120,45 +1217,104 @@ void *SEHExceptionThread(void *args)
         }
     }
 }
+       
+/*++
+Function :
+    MachExceptionInfo constructor
+
+    Saves the exception info from the exception notification message and
+    the current thread state.
+
+Parameters:
+    thread - thread port to restore
+    message - exception message
 
-void ForwardMachException(CPalThread *pThread, MachMessage *pMessage)
+Return value :
+    none
+--*/
+MachExceptionInfo::MachExceptionInfo(mach_port_t thread, MachMessage& message)
 {
-    thread_act_t hThread = pThread->GetMachPortSelf();
-    MachMessage sReplyOrForward;
+    kern_return_t machret;
 
-    // Locate the record of previously installed handlers that the target thread keeps.
-    CorUnix::CThreadMachExceptionHandlers *pHandlers = pThread->GetSavedMachHandlers();
+    ExceptionType = message.GetException();
+    SubcodeCount = message.GetExceptionCodeCount();
+    NONPAL_RETAIL_ASSERTE(SubcodeCount >= 0 && SubcodeCount <= 2);
 
-    // Check whether there's even a handler for the particular exception we've been handed.
-    CorUnix::MachExceptionHandler sHandler;
-    if (pHandlers->GetHandler(pMessage->GetException(), &sHandler))
-    {
-        NONPAL_TRACE("Forward request to port %08x\n", sHandler.m_handler);
+    for (int i = 0; i < SubcodeCount; i++)
+        Subcodes[i] = message.GetExceptionCode(i);
 
-        // Forward the notification
-        sReplyOrForward.ForwardNotification(&sHandler, pMessage);
+    mach_msg_type_number_t count = x86_THREAD_STATE_COUNT;
+    machret = thread_get_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, &count);
+    CHECK_MACH("thread_get_state", machret);
 
-        // Spin wait until this thread is hijacked or the process is aborted.
-        while (TRUE)
+    count = x86_FLOAT_STATE_COUNT;
+    machret = thread_get_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, &count);
+    CHECK_MACH("thread_get_state(float)", machret);
+
+    count = x86_DEBUG_STATE_COUNT;
+    machret = thread_get_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, &count);
+    CHECK_MACH("thread_get_state(debug)", machret);
+}
+
+/*++
+Function :
+    MachExceptionInfo::RestoreState
+
+    Restore the thread to the saved exception info state.
+
+Parameters:
+    thread - thread port to restore
+
+Return value :
+    none
+--*/
+void MachExceptionInfo::RestoreState(mach_port_t thread)
+{
+    // If we are restarting a breakpoint, we need to bump the IP back one to
+    // point at the actual int 3 instructions.
+    if (ExceptionType == EXC_BREAKPOINT)
+    {
+        if (Subcodes[0] == EXC_I386_BPT)
         {
-            sched_yield();
+#ifdef _X86_
+            ThreadState.uts.ts32.eip--;
+#elif defined(_AMD64_)
+            ThreadState.uts.ts64.__rip--;
+#else
+#error Platform not supported
+#endif
         }
     }
-    else
-    {
-        // There's no previous handler to forward this notification to.
-        NONPAL_TRACE("Unhandled exception and no chain-back - aborting process\n");
-        PROCAbort();
-    }
+    kern_return_t machret = thread_set_state(thread, x86_THREAD_STATE, (thread_state_t)&ThreadState, x86_THREAD_STATE_COUNT);
+    CHECK_MACH("thread_set_state(thread)", machret);
+
+    machret = thread_set_state(thread, x86_FLOAT_STATE, (thread_state_t)&FloatState, x86_FLOAT_STATE_COUNT);
+    CHECK_MACH("thread_set_state(float)", machret);
+
+    machret = thread_set_state(thread, x86_DEBUG_STATE, (thread_state_t)&DebugState, x86_DEBUG_STATE_COUNT);
+    CHECK_MACH("thread_set_state(debug)", machret);
 }
 
+/*++
+Function :
+    MachSetThreadContext
+
+    Sets the context of the current thread by sending a notification
+    to the exception thread.
+
+Parameters:
+    lpContext - the CONTEXT to set the current thread
+
+Return value :
+    Doesn't return
+--*/
 PAL_NORETURN 
-void MachSetThreadContext(CONTEXT *lpContext)
+void 
+MachSetThreadContext(CONTEXT *lpContext)
 {
-    // We need to send a message to the worker thread so that it can set our thread context
-    // It is responsible for deallocating the thread port.
+    // We need to send a message to the worker thread so that it can set our thread context.
     MachMessage sRequest;
-    sRequest.SendSetThread(s_ExceptionPort, mach_thread_self(), lpContext);
+    sRequest.SendSetThread(s_ExceptionPort, lpContext);
 
     // Make sure we don't do anything
     while (TRUE)
@@ -1179,47 +1335,70 @@ Return value :
     TRUE  if SEH support initialization succeeded
     FALSE otherwise
 --*/
-BOOL SEHInitializeMachExceptions (void)
+BOOL 
+SEHInitializeMachExceptions(void)
 {
-    kern_return_t MachRet;
-    int CreateRet;
     pthread_t exception_thread;
+    kern_return_t machret;
 
     // Allocate a mach port that will listen in on exceptions
-    MachRet = mach_port_allocate(mach_task_self(),
-                                 MACH_PORT_RIGHT_RECEIVE,
-                                 &s_ExceptionPort);
-
-    if (MachRet != KERN_SUCCESS)
+    machret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &s_ExceptionPort);
+    if (machret != KERN_SUCCESS)
     {
-        ASSERT("mach_port_allocate failed: %d\n", MachRet);
-        UTIL_SetLastErrorFromMach(MachRet);
+        ASSERT("mach_port_allocate failed: %d\n", machret);
+        UTIL_SetLastErrorFromMach(machret);
         return FALSE;
     }
 
     // Insert the send right into the task
-    MachRet = mach_port_insert_right(mach_task_self(),
-                                     s_ExceptionPort,
-                                     s_ExceptionPort,
-                                     MACH_MSG_TYPE_MAKE_SEND);
-
-    if (MachRet != KERN_SUCCESS)
+    machret = mach_port_insert_right(mach_task_self(), s_ExceptionPort, s_ExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
+    if (machret != KERN_SUCCESS)
     {
-        ASSERT("mach_port_insert_right failed: %d\n", MachRet);
-        UTIL_SetLastErrorFromMach(MachRet);
+        ASSERT("mach_port_insert_right failed: %d\n", machret);
+        UTIL_SetLastErrorFromMach(machret);
         return FALSE;
     }
 
     // Create the thread that will listen to the exception for all threads
-    CreateRet = pthread_create(&exception_thread, NULL, SEHExceptionThread, NULL);
-
-    if ( CreateRet != 0 )
+    int createret = pthread_create(&exception_thread, NULL, SEHExceptionThread, NULL);
+    if (createret != 0)
     {
-        ERROR("pthread_create failed, error is %d (%s)\n", CreateRet, strerror(CreateRet));
+        ERROR("pthread_create failed, error is %d (%s)\n", createret, strerror(createret));
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
 
+#ifdef _DEBUG
+    if (NONPAL_TRACE_ENABLED)
+    {
+        CThreadMachExceptionHandlers taskHandlers;
+        machret = task_get_exception_ports(mach_task_self(),
+            PAL_EXC_ALL_MASK,
+            taskHandlers.m_masks,
+            &taskHandlers.m_nPorts,
+            taskHandlers.m_handlers,
+            taskHandlers.m_behaviors,
+            taskHandlers.m_flavors);
+
+        if (machret == KERN_SUCCESS)
+        {
+            NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT count %d\n", taskHandlers.m_nPorts);
+            for (mach_msg_type_number_t i = 0; i < taskHandlers.m_nPorts; i++)
+            {
+                NONPAL_TRACE("SEHInitializeMachExceptions: TASK PORT mask %08x handler: %08x behavior %08x flavor %u\n",
+                    taskHandlers.m_masks[i],
+                    taskHandlers.m_handlers[i],
+                    taskHandlers.m_behaviors[i],
+                    taskHandlers.m_flavors[i]);
+            }
+        }
+        else
+        {
+            NONPAL_TRACE("SEHInitializeMachExceptions: task_get_exception_ports FAILED %d %s\n", machret, mach_error_string(machret));
+        }
+    }
+#endif // _DEBUG
+
 #ifndef FEATURE_PAL_SXS
     if (!SEHEnableMachExceptions())
     {
@@ -1282,7 +1461,8 @@ During PAL_Terminate, we reach a point where SEH isn't possible any more
 (handle manager is off, etc). Past that point, we can't avoid crashing on
 an exception.
 --*/
-void SEHCleanupExceptionPort(void)
+void 
+SEHCleanupExceptionPort(void)
 {
     TRACE("Restoring default exception ports\n");
 #ifndef FEATURE_PAL_SXS
@@ -1291,7 +1471,9 @@ void SEHCleanupExceptionPort(void)
     s_DebugInitialized = FALSE;
 }
 
-extern "C" void ActivationHandler(CONTEXT* context)
+extern "C" 
+void 
+ActivationHandler(CONTEXT* context)
 {
     if (g_activationFunction != NULL)
     {
@@ -1305,7 +1487,20 @@ extern "C" void ActivationHandler(CONTEXT* context)
 extern "C" void ActivationHandlerWrapper();
 extern "C" int ActivationHandlerReturnOffset;
 
-PAL_ERROR InjectActivationInternal(CPalThread* pThread)
+/*++
+Function :
+    InjectActivationInternal
+
+    Sets up the specified thread to call the ActivationHandler.
+
+Parameters:
+    pThread - PAL thread instance
+
+Return value :
+    PAL_ERROR
+--*/
+PAL_ERROR 
+InjectActivationInternal(CPalThread* pThread)
 {
     PAL_ERROR palError;
 
index b926175..c9f0276 100644 (file)
@@ -4,17 +4,13 @@
 
 /*++
 
-
-
 Module Name:
 
-    exception/machexception.h
+    machexception.h
 
 Abstract:
     Private mach exception handling utilities for SEH
 
-
-
 --*/
 
 #ifndef _MACHEXCEPTION_H_
@@ -31,18 +27,17 @@ extern "C"
 
 #define HIJACK_ON_SIGNAL 1
 
-// Process and thread Initialization/Cleanup routines
-BOOL SEHInitializeMachExceptions(void);
-void SEHCleanupExceptionPort (void);
-void MachExceptionInitializeDebug(void);
-
 // List of exception types we will be watching for
 // NOTE: if you change any of these, you need to adapt s_nMachExceptionPortsMax in thread.hpp
-#define PAL_EXC_ILLEGAL_MASK   (EXC_MASK_BAD_INSTRUCTION|EXC_MASK_EMULATION)
-#define PAL_EXC_DEBUGGING_MASK (EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE)
-#define PAL_EXC_MANAGED_MASK   (EXC_MASK_BAD_ACCESS|EXC_MASK_ARITHMETIC)
-#define PAL_EXC_ALL_MASK       (PAL_EXC_ILLEGAL_MASK|PAL_EXC_DEBUGGING_MASK|PAL_EXC_MANAGED_MASK)
+#define PAL_EXC_ILLEGAL_MASK   (EXC_MASK_BAD_INSTRUCTION | EXC_MASK_EMULATION)
+#define PAL_EXC_DEBUGGING_MASK (EXC_MASK_BREAKPOINT | EXC_MASK_SOFTWARE)
+#define PAL_EXC_MANAGED_MASK   (EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC)
+#define PAL_EXC_ALL_MASK       (PAL_EXC_ILLEGAL_MASK | PAL_EXC_DEBUGGING_MASK | PAL_EXC_MANAGED_MASK)
 
+// Process and thread initialization/cleanup/context routines
+BOOL SEHInitializeMachExceptions(void);
+void SEHCleanupExceptionPort (void);
+void MachExceptionInitializeDebug(void);
 PAL_NORETURN void MachSetThreadContext(CONTEXT *lpContext);
 
 #ifdef __cplusplus
index 52fedc5..2e11da8 100644 (file)
@@ -4,18 +4,14 @@
 
 /*++
 
-
-
 Module Name:
 
-    machexception.cpp
+    machmessage.cpp
 
 Abstract:
 
     Abstraction over Mach messages used during exception handling.
 
-
-
 --*/
 
 #include "config.h"
@@ -25,38 +21,6 @@ Abstract:
 
 #if HAVE_MACH_EXCEPTIONS
 
-// The vast majority of Mach calls we make in this module are critical: we cannot recover from failures of
-// these methods (principally because we're handling hardware exceptions in the context of a single dedicated
-// handler thread). The following macro encapsulates checking the return code from Mach methods (we always
-// name this 'machret' for consistency) and emitting some useful data and aborting the process on failure.
-#define MACH_CHECK(_msg) do {                                           \
-        if (machret != KERN_SUCCESS)                                    \
-        {                                                               \
-            char _szError[1024];                                        \
-            sprintf(_szError, "%s: %u: %s", __FUNCTION__, __LINE__, _msg); \
-            mach_error(_szError, machret);                              \
-            abort();                                                    \
-        }                                                               \
-    } while (false)
-
-// This macro terminates the process with some useful debug info as above, but for the general failure points
-// that have nothing to do with Mach.
-#define FATAL_ERROR(_msg, ...) do {                                         \
-        printf("%s: %u: " _msg "\n", __FUNCTION__, __LINE__, ## __VA_ARGS__); \
-        abort();                                                        \
-    } while (false)
-
-#ifdef _DEBUG
-// Assert macro that doesn't rely on the PAL.
-#define MACHMESSAGE_ASSERT(_expr) do {                      \
-        if (!(_expr))                                       \
-            FATAL_ERROR("ASSERTION FAILURE: %s\n", #_expr); \
-    } while (false)
-#else // _DEBUG
-#define MACHMESSAGE_ASSERT(_expr)
-#endif // _DEBUG
-
-
 // Construct an empty message. Use Receive() to form a message that can be inspected or SendSetThread(),
 // ForwardNotification(), ReplyToNotification() or ForwardReply() to construct a message and sent it.
 MachMessage::MachMessage()
@@ -65,14 +29,6 @@ MachMessage::MachMessage()
     ResetMessage();
 }
 
-void MachMessage::InitializeFrom(const MachMessage& source)
-{
-    m_fPortsOwned = false;
-    ResetMessage();
-
-    memcpy(&m_rgMessageBuffer, &source.m_rgMessageBuffer, sizeof(m_rgMessageBuffer));
-}
-
 // Listen for the next message on the given port and initialize this class with the contents. The message type
 // must match one of the MessageTypes indicated above (or the process will be aborted).
 void MachMessage::Receive(mach_port_t hPort)
@@ -90,12 +46,13 @@ void MachMessage::Receive(mach_port_t hPort)
                        hPort,
                        MACH_MSG_TIMEOUT_NONE,
                        MACH_PORT_NULL);
-    MACH_CHECK("mach_msg()");
+    CHECK_MACH("mach_msg()", machret);
 
     // Check it's one of the messages we're expecting.
     switch (m_pMessage->header.msgh_id)
     {
     case SET_THREAD_MESSAGE_ID:
+    case FORWARD_EXCEPTION_MESSAGE_ID:
     case EXCEPTION_RAISE_MESSAGE_ID:
     case EXCEPTION_RAISE_STATE_MESSAGE_ID:
     case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
@@ -108,9 +65,10 @@ void MachMessage::Receive(mach_port_t hPort)
     case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
     case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
     case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+    case NOTIFY_SEND_ONCE_MESSAGE_ID:
         break;
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
     
     m_fPortsOwned = true;
@@ -122,6 +80,18 @@ bool MachMessage::IsSetThreadRequest()
     return m_pMessage->header.msgh_id == SET_THREAD_MESSAGE_ID;
 }
 
+// Indicates whether the message is a request to forward the exception
+bool MachMessage::IsForwardExceptionRequest()
+{
+    return m_pMessage->header.msgh_id == FORWARD_EXCEPTION_MESSAGE_ID;
+}
+
+// Indicates whether the message is a notification that a send-once message was destroyed by the receiver.
+bool MachMessage::IsSendOnceDestroyedNotify()
+{
+    return m_pMessage->header.msgh_id == NOTIFY_SEND_ONCE_MESSAGE_ID;
+}
+
 // Indicates whether the message is a notification of an exception.
 bool MachMessage::IsExceptionNotification()
 {
@@ -169,6 +139,8 @@ const char *MachMessage::GetMessageTypeName()
     {
     case SET_THREAD_MESSAGE_ID:
         return "SET_THREAD";
+    case FORWARD_EXCEPTION_MESSAGE_ID:
+        return "FORWARD_EXCEPTION";
     case EXCEPTION_RAISE_MESSAGE_ID:
         return "EXCEPTION_RAISE";
     case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
@@ -223,6 +195,10 @@ void MachMessage::GetPorts(bool fCalculate, bool fValidThread)
     case SET_THREAD_MESSAGE_ID:
         m_hThread = m_pMessage->data.set_thread.thread;
         break;
+
+    case FORWARD_EXCEPTION_MESSAGE_ID:
+        m_hThread = m_pMessage->data.forward_exception.thread;
+        break;
     
     case EXCEPTION_RAISE_MESSAGE_ID:
         m_hThread = m_pMessage->data.raise.thread_port.name;
@@ -265,18 +241,18 @@ void MachMessage::GetPorts(bool fCalculate, bool fValidThread)
     default:
         if (fValidThread)
         {
-            FATAL_ERROR("Can only get thread from notification message.");
+            NONPAL_RETAIL_ASSERT("Can only get thread from notification message.");
         }
         break;
     }
 }
 
-// Get the properties of a set thread request. Fills in the provided context structure with the context from
-// the message and returns the target thread to which the context should be applied.
+// Get the properties of a set thread or forward exception request. Fills in the provided 
+// context structure with the context from the message and returns the target thread to 
+// which the context should be applied.
 thread_act_t MachMessage::GetThreadContext(CONTEXT *pContext)
 {
-    if (m_pMessage->header.msgh_id != SET_THREAD_MESSAGE_ID)
-        FATAL_ERROR("Unhandled message type for GetThreadContext(): %u", m_pMessage->header.msgh_id);
+    NONPAL_ASSERTE(IsSetThreadRequest());
 
     memcpy(pContext, &m_pMessage->data.set_thread.new_context, sizeof(CONTEXT));
     m_hThread = m_pMessage->data.set_thread.thread;
@@ -314,7 +290,7 @@ exception_type_t MachMessage::GetException()
         return m_pMessage->data.raise_state_identity_64.exception;
 
     default:
-        FATAL_ERROR("Can only get exception from notification message.");
+        NONPAL_RETAIL_ASSERT("Can only get exception from notification message.");
     }
 }
 
@@ -342,7 +318,7 @@ int MachMessage::GetExceptionCodeCount()
         return m_pMessage->data.raise_state_identity_64.code_count;
 
     default:
-        FATAL_ERROR("Can only get exception code count from notification message.");
+        NONPAL_RETAIL_ASSERT("Can only get exception code count from notification message.");
     }
 }
 
@@ -351,31 +327,31 @@ MACH_EH_TYPE(exception_data_type_t) MachMessage::GetExceptionCode(int iIndex)
 {
     if (iIndex < 0 || iIndex >= GetExceptionCodeCount())
     {
-        FATAL_ERROR("GetExceptionCode() index out of range.");
+        NONPAL_RETAIL_ASSERT("GetExceptionCode() index out of range.");
     }
 
     switch (m_pMessage->header.msgh_id)
     {
     case EXCEPTION_RAISE_MESSAGE_ID:
-        return (int)m_pMessage->data.raise.code[iIndex];
+        return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise.code[iIndex];
 
     case EXCEPTION_RAISE_64_MESSAGE_ID:
         return m_pMessage->data.raise_64.code[iIndex];
 
     case EXCEPTION_RAISE_STATE_MESSAGE_ID:
-        return (int)m_pMessage->data.raise_state.code[iIndex];
+        return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise_state.code[iIndex];
 
     case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
         return m_pMessage->data.raise_state_64.code[iIndex];
 
     case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
-        return (int)m_pMessage->data.raise_state_identity.code[iIndex];
+        return (MACH_EH_TYPE(exception_data_type_t))m_pMessage->data.raise_state_identity.code[iIndex];
 
     case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
         return m_pMessage->data.raise_state_identity_64.code[iIndex];
 
     default:
-        FATAL_ERROR("Can only get exception code from notification message.");
+        NONPAL_RETAIL_ASSERT("Can only get exception code from notification message.");
     }
 }
 
@@ -416,7 +392,7 @@ thread_state_flavor_t MachMessage::GetThreadStateFlavor()
         return m_pMessage->data.raise_state_identity_reply_64.flavor;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
@@ -424,10 +400,10 @@ thread_state_flavor_t MachMessage::GetThreadStateFlavor()
 // doesn't contain a thread state or the flavor of the state in the message doesn't match, the state will be
 // fetched directly from the target thread instead (which can be computed implicitly for exception messages or
 // passed explicitly for reply messages).
-size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t hThread)
+mach_msg_type_number_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t thread)
 {
+    mach_msg_type_number_t count;
     kern_return_t machret;
-    size_t cbState;
 
     switch (m_pMessage->header.msgh_id)
     {
@@ -444,11 +420,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state.old_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state.old_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state.old_state_count;
+            memcpy(pState, m_pMessage->data.raise_state.old_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -459,11 +433,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_64.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_64.old_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_64.old_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_64.old_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_64.old_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -474,11 +446,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_identity.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_identity.old_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_identity.old_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_identity.old_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_identity.old_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -489,11 +459,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_identity_64.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_identity_64.old_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_identity_64.old_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_identity_64.old_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_identity_64.old_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -504,11 +472,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_reply.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_reply.new_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_reply.new_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_reply.new_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_reply.new_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -519,11 +485,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_reply_64.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_reply_64.new_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_reply_64.new_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_reply_64.new_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_reply_64.new_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -534,11 +498,9 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_identity_reply.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_identity_reply.new_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_identity_reply.new_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_identity_reply.new_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_identity_reply.new_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
@@ -549,30 +511,56 @@ size_t MachMessage::GetThreadState(thread_state_flavor_t eFlavor, thread_state_t
         // after (if not we'll fall through and get the correct flavor below).
         if (m_pMessage->data.raise_state_identity_reply_64.flavor == eFlavor)
         {
-            cbState = m_pMessage->data.raise_state_identity_reply_64.new_state_count * sizeof(natural_t);
-            memcpy(pState,
-                   m_pMessage->data.raise_state_identity_reply_64.new_state,
-                   cbState);
-            return cbState;
+            count = m_pMessage->data.raise_state_identity_reply_64.new_state_count;
+            memcpy(pState, m_pMessage->data.raise_state_identity_reply_64.new_state, count * sizeof(natural_t));
+            return count;
         }
         break;
     }
 
     default:
-        FATAL_ERROR("Unsupported message type for requesting thread state.");
+        NONPAL_RETAIL_ASSERT("Unsupported message type for requesting thread state.");
     }
 
     // No state in the message or the flavor didn't match. Get the requested flavor of state directly from the
     // thread instead.
-    mach_msg_type_number_t iStateCount = THREAD_STATE_MAX;
-    machret = thread_get_state(hThread ? hThread : GetThread(), eFlavor, (thread_state_t)pState, &iStateCount);
-    MACH_CHECK("thread_get_state()");
+    count = THREAD_STATE_MAX;
+    machret = thread_get_state(thread ? thread : GetThread(), eFlavor, (thread_state_t)pState, &count);
+    CHECK_MACH("thread_get_state()", machret);
+
+    return count;
+}
+
+// Fetch the return code from a reply type message.
+kern_return_t MachMessage::GetReturnCode()
+{
+    switch (m_pMessage->header.msgh_id)
+    {
+    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
+        return m_pMessage->data.raise_reply.ret;
+
+    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
+        return m_pMessage->data.raise_reply_64.ret;
+
+    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
+        return m_pMessage->data.raise_state_reply.ret;
+
+    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
+        return m_pMessage->data.raise_state_reply_64.ret;
 
-    return iStateCount * sizeof(natural_t);
+    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
+        return m_pMessage->data.raise_state_identity_reply.ret;
+
+    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
+        return m_pMessage->data.raise_state_identity_reply_64.ret;
+
+    default:
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
+    }
 }
 
 // Initialize and send a request to set the register context of a particular thread.
-void MachMessage::SendSetThread(mach_port_t hServerPort, thread_act_t hThread, CONTEXT *pContext)
+void MachMessage::SendSetThread(mach_port_t hServerPort, CONTEXT *pContext)
 {
     kern_return_t machret;
 
@@ -583,8 +571,8 @@ void MachMessage::SendSetThread(mach_port_t hServerPort, thread_act_t hThread, C
     // set above).
     InitFixedFields();
 
-    // Initialize type-specific fields.
-    m_pMessage->data.set_thread.thread = hThread;
+    // Initialize type-specific fields. The receiving end is responsible for deallocating the thread port.
+    m_pMessage->data.set_thread.thread = mach_thread_self();
     memcpy(&m_pMessage->data.set_thread.new_context, pContext, sizeof(CONTEXT));
 
     // Initialize header fields.
@@ -604,18 +592,70 @@ void MachMessage::SendSetThread(mach_port_t hServerPort, thread_act_t hThread, C
                        MACH_PORT_NULL,
                        MACH_MSG_TIMEOUT_NONE,
                        MACH_PORT_NULL);
-    MACH_CHECK("mach_msg()");
+    CHECK_MACH("mach_msg()", machret);
 
     // Erase any stale data. (This may not finish executing; nothing is needed to be freed here.)
     ResetMessage();
 }
 
-// Initialize the message to represent a forwarded version of the given
-// exception notification message and send that message to the chain-back handler previously registered for
-// the exception type being notified. The new message takes account of the fact that the target handler may
-// not have requested the same notification behavior or flavor as our handler. A new Mach port is created to
-// receive the reply, and this port is returned to the caller. Clean up the message afterwards.
-void MachMessage::ForwardNotification(CorUnix::MachExceptionHandler *pHandler, MachMessage *pNotification)
+void MachMessage::SendForwardException(mach_port_t hServerPort, MachExceptionInfo *pExceptionInfo, CPalThread *ppalThread)
+{
+    kern_return_t machret;
+
+    // Set the message type.
+    m_pMessage->header.msgh_id = FORWARD_EXCEPTION_MESSAGE_ID;
+
+    // Initialize the fields that don't need any further input (this depends on the message type having been
+    // set above).
+    InitFixedFields();
+
+    // Initialize type-specific fields. The receiving end is responsible for deallocating the thread port.
+    m_pMessage->data.forward_exception.thread = mach_thread_self();
+    m_pMessage->data.forward_exception.ppalThread = ppalThread;
+    memcpy(&m_pMessage->data.forward_exception.exception_info, pExceptionInfo, sizeof(MachExceptionInfo));
+
+    // Initialize header fields.
+    m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+    m_pMessage->header.msgh_remote_port = hServerPort;      // Destination port
+    m_pMessage->header.msgh_local_port = MACH_PORT_NULL;    // We expect no reply
+
+    // Set the message header size field based on the contents of the message (call this function after all
+    // other fields have been initialized).
+    InitMessageSize();
+
+    // Send the formatted message.
+    machret = mach_msg((mach_msg_header_t*)m_pMessage,
+                       MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+                       m_pMessage->header.msgh_size,
+                       0,
+                       MACH_PORT_NULL,
+                       MACH_MSG_TIMEOUT_NONE,
+                       MACH_PORT_NULL);
+    CHECK_MACH("mach_msg()", machret);
+
+    // Erase any stale data.
+    ResetMessage();
+}
+
+// Returns the pal thread instance for the forward exception message
+CPalThread *MachMessage::GetPalThread()
+{
+    NONPAL_ASSERTE(IsForwardExceptionRequest());
+    return m_pMessage->data.forward_exception.ppalThread;
+}
+
+MachExceptionInfo *MachMessage::GetExceptionInfo()
+{
+    NONPAL_ASSERTE(IsForwardExceptionRequest());
+    return &m_pMessage->data.forward_exception.exception_info;
+}
+
+// Initialize the message to represent a forwarded version of the given exception notification message and
+// send that message to the chain-back handler previously registered for the exception type being notified.
+// The new message takes account of the fact that the target handler may not have requested the same notification
+// behavior or flavor as our handler. A new Mach port is created to receive the reply, and this port is returned
+// to the caller. Clean up the message afterwards.
+void MachMessage::ForwardNotification(MachExceptionHandler *pHandler, MachMessage& message)
 {
     kern_return_t machret;
 
@@ -630,28 +670,26 @@ void MachMessage::ForwardNotification(CorUnix::MachExceptionHandler *pHandler, M
     // the two messages may be in different formats (e.g. RAISE vs RAISE_STATE). We silently drop data that is
     // not needed in the outgoing message and synthesize any required data that is not present in the incoming
     // message.
-    SetThread(pNotification->GetThread());
-    SetException(pNotification->GetException());
+    SetThread(message.GetThread());
+    SetException(message.GetException());
 
-    int cCodes = pNotification->GetExceptionCodeCount();
+    int cCodes = message.GetExceptionCodeCount();
     SetExceptionCodeCount(cCodes);
     for (int i = 0; i < cCodes; i++)
-        SetExceptionCode(i, pNotification->GetExceptionCode(i));
-
-    NONPAL_TRACE("ForwardNotification: handler thread flavor %04x\n", pHandler->m_flavor);
+        SetExceptionCode(i, message.GetExceptionCode(i));
 
     // Don't bother fetching thread state unless the destination actually requires it.
     if (pHandler->m_flavor != THREAD_STATE_NONE)
     {
-        thread_state_data_t sThreadState;
-        size_t cbState = pNotification->GetThreadState(pHandler->m_flavor, (thread_state_t)&sThreadState);
-        SetThreadState(pHandler->m_flavor, (thread_state_t)&sThreadState, cbState);
+        thread_state_data_t threadState;
+        mach_msg_type_number_t count = message.GetThreadState(pHandler->m_flavor, (thread_state_t)&threadState);
+        SetThreadState(pHandler->m_flavor, (thread_state_t)&threadState, count);
     }
 
     // Initialize header fields.
-    m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
-    m_pMessage->header.msgh_remote_port = pHandler->m_handler;              // Forward to here
-    m_pMessage->header.msgh_local_port = pNotification->GetLocalPort();     // The reply will come here
+    m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+    m_pMessage->header.msgh_remote_port = pHandler->m_handler;          // Forward to here
+    m_pMessage->header.msgh_local_port = message.GetRemotePort();       // The reply will come here
 
     // Set the message header size field based on the contents of the message (call this function after all
     // other fields have been initialized).
@@ -665,22 +703,22 @@ void MachMessage::ForwardNotification(CorUnix::MachExceptionHandler *pHandler, M
                        MACH_PORT_NULL,
                        MACH_MSG_TIMEOUT_NONE,
                        MACH_PORT_NULL);
-    MACH_CHECK("mach_msg()");
+    CHECK_MACH("mach_msg()", machret);
 
     // Erase any stale data.
     ResetMessage();
 }
 
-// Initialize the message to represent a reply to the given exception
-// notification and send that reply back to the original sender of the notification. This is used when our
-// handler handles the exception rather than forwarding it to a chain-back handler.
+// Initialize the message to represent a reply to the given exception notification message
+// and send that reply back to the original sender of the notification. This is used when 
+// our handler handles the exception rather than forwarding it to a chain-back handler.
 // Clean up the message afterwards.
-void MachMessage::ReplyToNotification(MachMessage *pNotification, kern_return_t eResult)
+void MachMessage::ReplyToNotification(MachMessage& message, kern_return_t eResult)
 {
     kern_return_t machret;
 
     // Set the message type.
-    m_pMessage->header.msgh_id = MapNotificationToReplyType(pNotification->m_pMessage->header.msgh_id);
+    m_pMessage->header.msgh_id = MapNotificationToReplyType(message.m_pMessage->header.msgh_id);
 
     // Initialize the fields that don't need any further input (this depends on the message type having been
     // set above).
@@ -688,26 +726,22 @@ void MachMessage::ReplyToNotification(MachMessage *pNotification, kern_return_t
 
     SetReturnCode(eResult);
 
-    thread_state_flavor_t eNotificationFlavor = pNotification->GetThreadStateFlavor();
+    thread_state_flavor_t eNotificationFlavor = message.GetThreadStateFlavor();
     if (eNotificationFlavor != THREAD_STATE_NONE)
     {
         // If the reply requires a thread state be sure to get it from the thread directly rather than the
         // notification message (handling the exception is likely to have changed the thread state).
-        thread_state_data_t sThreadState;
-        mach_msg_type_number_t iStateCount = THREAD_STATE_MAX;
-        machret = thread_get_state(pNotification->GetThread(),
-                                   eNotificationFlavor,
-                                   (thread_state_t)&sThreadState,
-                                   &iStateCount);
-        MACH_CHECK("thread_get_state()");
-
-        SetThreadState(eNotificationFlavor, (thread_state_t)&sThreadState, iStateCount * sizeof(natural_t));
+        thread_state_data_t threadState;
+        mach_msg_type_number_t count = THREAD_STATE_MAX;
+        machret = thread_get_state(message.GetThread(), eNotificationFlavor, (thread_state_t)&threadState, &count);
+
+        SetThreadState(eNotificationFlavor, (thread_state_t)&threadState, count);
     }
 
     // Initialize header fields.
     m_pMessage->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0);
-    m_pMessage->header.msgh_remote_port = pNotification->GetRemotePort(); // Reply goes back to sender
-    m_pMessage->header.msgh_local_port = 0;                               // No reply to this expected
+    m_pMessage->header.msgh_remote_port = message.GetRemotePort();     // Reply goes back to sender
+    m_pMessage->header.msgh_local_port = 0;                                 // No reply to this expected
 
     // Set the message header size field based on the contents of the message (call this function after all
     // other fields have been initialized).
@@ -721,7 +755,7 @@ void MachMessage::ReplyToNotification(MachMessage *pNotification, kern_return_t
                        MACH_PORT_NULL,
                        MACH_MSG_TIMEOUT_NONE,
                        MACH_PORT_NULL);
-    MACH_CHECK("mach_msg()");
+    CHECK_MACH("mach_msg()", machret);
 
     // Erase any stale data.
     ResetMessage();
@@ -739,13 +773,13 @@ void MachMessage::ResetMessage()
         if (m_hThread != MACH_PORT_NULL)
         {
             machret = mach_port_deallocate(mach_task_self(), m_hThread);
-            MACH_CHECK("mach_port_deallocate(m_hThread)");
+            CHECK_MACH("mach_port_deallocate(m_hThread)", machret);
         }
         
         if (m_hTask != MACH_PORT_NULL)
         {
             machret = mach_port_deallocate(mach_task_self(), m_hTask);
-            MACH_CHECK("mach_port_deallocate(m_hTask)");
+            CHECK_MACH("mach_port_deallocate(m_hTask)", machret);
         }
     }
 
@@ -768,6 +802,9 @@ void MachMessage::InitFixedFields()
     case SET_THREAD_MESSAGE_ID:
         break;
 
+    case FORWARD_EXCEPTION_MESSAGE_ID:
+        break;
+
     case EXCEPTION_RAISE_MESSAGE_ID:
         m_pMessage->data.raise.msgh_body.msgh_descriptor_count = 0;
         m_pMessage->data.raise.ndr = NDR_record;
@@ -845,7 +882,7 @@ void MachMessage::InitFixedFields()
         break;
 
     default:
-        FATAL_ERROR("Unhandled message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
     }
 
     m_pMessage->header.msgh_reserved = 0;
@@ -873,6 +910,10 @@ void MachMessage::InitMessageSize()
         m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(set_thread_request_t);
         break;
 
+    case FORWARD_EXCEPTION_MESSAGE_ID:
+        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(forward_exception_request_t);
+        break;
+
     case EXCEPTION_RAISE_MESSAGE_ID:
         m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_notification_t);
         break;
@@ -938,7 +979,7 @@ void MachMessage::InitMessageSize()
         break;
 
     default:
-        FATAL_ERROR("Unhandled message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
@@ -955,96 +996,75 @@ thread_act_t MachMessage::GetThreadFromState(thread_state_flavor_t eFlavor, thre
     // thread).
     switch (eFlavor)
     {
-    case x86_THREAD_STATE32:
 #ifdef _X86_
-        targetSP = ((x86_thread_state32_t*)pState)->esp;
-#elif defined(_AMD64_)
-        targetSP = ((x86_thread_state32_t*)pState)->__esp;
-#else
-#error Unexpected architecture.
-#endif
-        break;
-
     case x86_THREAD_STATE:
-#ifdef _X86_
         targetSP = ((x86_thread_state_t*)pState)->uts.ts32.esp;
+        break;
+
+    case x86_THREAD_STATE32:
+        targetSP = ((x86_thread_state32_t*)pState)->esp;
+        break;
 #elif defined(_AMD64_)
+    case x86_THREAD_STATE:
         targetSP = ((x86_thread_state_t*)pState)->uts.ts64.__rsp;
-#else
-#error Unexpected architecture.
-#endif
         break;
 
-#ifdef _AMD64_
     case x86_THREAD_STATE64:
         targetSP = ((x86_thread_state64_t*)pState)->__rsp;
         break;
-#endif // _AMD64_
-        
+#else
+#error Unexpected architecture.
+#endif
     default:
-        FATAL_ERROR("Unhandled thread state flavor: %u", eFlavor);
+        NONPAL_RETAIL_ASSERT("Unhandled thread state flavor: %u", eFlavor);
     }
 
     // Capture the list of threads in the current task. Obviously this changes asynchronously to us, but that
     // doesn't matter since we know the thread we're after is suspended in the kernel and can't go anywhere.
     mach_msg_type_number_t cThreads;
     thread_act_t *pThreads;
-    kern_return_t machret = task_threads(mach_task_self(),
-                                         &pThreads,
-                                         &cThreads);
-    MACH_CHECK("task_threads()");
+    kern_return_t machret = task_threads(mach_task_self(), &pThreads, &cThreads);
+    CHECK_MACH("task_threads()", machret);
 
     // Iterate through each of the threads in the list.
     for (mach_msg_type_number_t i = 0; i < cThreads; i++)
     {
         // Get the general register state of each thread.
-#ifdef _X86_        
-        x86_thread_state32_t sThreadState;
-        const thread_state_flavor_t sThreadStateFlavor = x86_THREAD_STATE32;
-#elif defined(_AMD64_)
-        x86_thread_state64_t sThreadState;
-        const thread_state_flavor_t sThreadStateFlavor = x86_THREAD_STATE64;
-#else
-#error Unexpected architecture.
-#endif
-        mach_msg_type_number_t cThreadState = sizeof(sThreadState) / sizeof(natural_t);
-        if (thread_get_state(pThreads[i],
-                             sThreadStateFlavor,
-                             (thread_state_t)&sThreadState,
-                             &cThreadState) == KERN_SUCCESS)
+        x86_thread_state_t threadState;
+        mach_msg_type_number_t count = x86_THREAD_STATE_COUNT;
+        machret = thread_get_state(pThreads[i], x86_THREAD_STATE, (thread_state_t)&threadState, &count);
+        if (machret == KERN_SUCCESS)
         {
             // If a thread has the same SP as our target it should be the same thread (otherwise we have two
             // threads sharing the same stack which is very bad). Conversely the thread we're looking for is
             // suspended in the kernel so its SP should not change. We should always be able to find an exact
             // match as a result.
 #ifdef _X86_
-            if (sThreadState.esp == targetSP)
+            if (threadState.uts.ts32.esp == targetSP)
 #elif defined(_AMD64_)
-            if (sThreadState.__rsp == targetSP)
+            if (threadState.uts.ts64.__rsp == targetSP)
 #else
 #error Unexpected architecture.
 #endif
             {
-                thread_act_t hThread = pThreads[i];
+                thread_act_t thread = pThreads[i];
                 
                 // Increment the refcount; the thread is a "send" right.
-                machret = mach_port_mod_refs(mach_task_self(), hThread, MACH_PORT_RIGHT_SEND, 1);
-                MACH_CHECK("mach_port_mod_refs()");
+                machret = mach_port_mod_refs(mach_task_self(), thread, MACH_PORT_RIGHT_SEND, 1);
+                CHECK_MACH("mach_port_mod_refs()", machret);
 
                 // Deallocate the thread list now we're done with it.
-                machret = vm_deallocate(mach_task_self(),
-                                        (vm_address_t)pThreads,
-                                        cThreads * sizeof(thread_act_t));
-                MACH_CHECK("vm_deallocate()");
+                machret = vm_deallocate(mach_task_self(), (vm_address_t)pThreads, cThreads * sizeof(thread_act_t));
+                CHECK_MACH("vm_deallocate()", machret);
 
                 // Return the thread we found.
-                return hThread;
+                return thread;
             }
         }
     }
 
     // If we got here no thread matched. That shouldn't be possible.
-    FATAL_ERROR("Failed to locate thread from state.");
+    NONPAL_RETAIL_ASSERT("Failed to locate thread from state.");
 }
 
 // Transform a exception handler behavior type into the corresponding Mach message ID for the notification.
@@ -1065,7 +1085,7 @@ mach_msg_id_t MachMessage::MapBehaviorToNotificationType(exception_behavior_t eB
     case MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY:
         return EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID;
     default:
-        FATAL_ERROR("Unsupported exception behavior type: %u", eBehavior);
+        NONPAL_RETAIL_ASSERT("Unsupported exception behavior type: %u", eBehavior);
     }
 }
 
@@ -1087,47 +1107,19 @@ mach_msg_id_t MachMessage::MapNotificationToReplyType(mach_msg_id_t eNotificatio
     case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
         return EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID;
     default:
-        FATAL_ERROR("Unsupported message type: %u", eNotificationType);
-    }
-}
-
-// Fetch the return code from a reply type message.
-kern_return_t MachMessage::GetReturnCode()
-{
-    switch (m_pMessage->header.msgh_id)
-    {
-    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
-        return m_pMessage->data.raise_reply.ret;
-
-    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
-        return m_pMessage->data.raise_reply_64.ret;
-
-    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
-        return m_pMessage->data.raise_state_reply.ret;
-
-    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
-        return m_pMessage->data.raise_state_reply_64.ret;
-
-    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
-        return m_pMessage->data.raise_state_identity_reply.ret;
-
-    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
-        return m_pMessage->data.raise_state_identity_reply_64.ret;
-
-    default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", eNotificationType);
     }
 }
 
 // Set faulting thread in an exception notification message.
-void MachMessage::SetThread(thread_act_t hThread)
+void MachMessage::SetThread(thread_act_t thread)
 {
-    bool fSet;
+    bool fSet = false;
 
     switch (m_pMessage->header.msgh_id)
     {
     case EXCEPTION_RAISE_MESSAGE_ID:
-        m_pMessage->data.raise.thread_port.name = hThread;
+        m_pMessage->data.raise.thread_port.name = thread;
         m_pMessage->data.raise.thread_port.pad1 = 0;
         m_pMessage->data.raise.thread_port.pad2 = 0;
         m_pMessage->data.raise.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
@@ -1136,7 +1128,7 @@ void MachMessage::SetThread(thread_act_t hThread)
         break;
 
     case EXCEPTION_RAISE_64_MESSAGE_ID:
-        m_pMessage->data.raise_64.thread_port.name = hThread;
+        m_pMessage->data.raise_64.thread_port.name = thread;
         m_pMessage->data.raise_64.thread_port.pad1 = 0;
         m_pMessage->data.raise_64.thread_port.pad2 = 0;
         m_pMessage->data.raise_64.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
@@ -1147,11 +1139,10 @@ void MachMessage::SetThread(thread_act_t hThread)
     case EXCEPTION_RAISE_STATE_MESSAGE_ID:
     case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
         // No thread field in RAISE_STATE messages.
-        fSet = false;
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
-        m_pMessage->data.raise_state_identity.thread_port.name = hThread;
+        m_pMessage->data.raise_state_identity.thread_port.name = thread;
         m_pMessage->data.raise_state_identity.thread_port.pad1 = 0;
         m_pMessage->data.raise_state_identity.thread_port.pad2 = 0;
         m_pMessage->data.raise_state_identity.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
@@ -1160,7 +1151,7 @@ void MachMessage::SetThread(thread_act_t hThread)
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
-        m_pMessage->data.raise_state_identity_64.thread_port.name = hThread;
+        m_pMessage->data.raise_state_identity_64.thread_port.name = thread;
         m_pMessage->data.raise_state_identity_64.thread_port.pad1 = 0;
         m_pMessage->data.raise_state_identity_64.thread_port.pad2 = 0;
         m_pMessage->data.raise_state_identity_64.thread_port.disposition = MACH_MSG_TYPE_COPY_SEND;
@@ -1169,15 +1160,14 @@ void MachMessage::SetThread(thread_act_t hThread)
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
-        fSet = false;
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
     
     if (fSet)
     {
         // Addref the thread port.
         kern_return_t machret;
-        machret = mach_port_mod_refs(mach_task_self(), hThread, MACH_PORT_RIGHT_SEND, 1);
+        machret = mach_port_mod_refs(mach_task_self(), thread, MACH_PORT_RIGHT_SEND, 1);
     }
 }
 
@@ -1211,7 +1201,7 @@ void MachMessage::SetException(exception_type_t eException)
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
@@ -1245,7 +1235,7 @@ void MachMessage::SetExceptionCodeCount(int cCodes)
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
@@ -1253,7 +1243,7 @@ void MachMessage::SetExceptionCodeCount(int cCodes)
 void MachMessage::SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_t) iCode)
 {
     if (iIndex < 0 || iIndex > 1)
-        FATAL_ERROR("Exception code index out of range");
+        NONPAL_RETAIL_ASSERT("Exception code index out of range");
 
     // Note that although the 64-bit message variants support 64-bit exception sub-codes the CoreCLR only
     // supports 32-bit processes. We should never see the upper 32-bits containing a non-zero value therefore.
@@ -1285,7 +1275,7 @@ void MachMessage::SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
@@ -1319,12 +1309,12 @@ void MachMessage::SetReturnCode(kern_return_t eReturnCode)
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
 // Set faulting thread register state in an exception notification or reply message.
-void MachMessage::SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, size_t cbState)
+void MachMessage::SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, mach_msg_type_number_t count)
 {
     switch (m_pMessage->header.msgh_id)
     {
@@ -1337,54 +1327,54 @@ void MachMessage::SetThreadState(thread_state_flavor_t eFlavor, thread_state_t p
 
     case EXCEPTION_RAISE_STATE_MESSAGE_ID:
         m_pMessage->data.raise_state.flavor = eFlavor;
-        m_pMessage->data.raise_state.old_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state.old_state, pState, cbState);
+        m_pMessage->data.raise_state.old_state_count = count;
+        memcpy(m_pMessage->data.raise_state.old_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
         m_pMessage->data.raise_state_64.flavor = eFlavor;
-        m_pMessage->data.raise_state_64.old_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_64.old_state, pState, cbState);
+        m_pMessage->data.raise_state_64.old_state_count = count;
+        memcpy(m_pMessage->data.raise_state_64.old_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
         m_pMessage->data.raise_state_identity.flavor = eFlavor;
-        m_pMessage->data.raise_state_identity.old_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_identity.old_state, pState, cbState);
+        m_pMessage->data.raise_state_identity.old_state_count = count;
+        memcpy(m_pMessage->data.raise_state_identity.old_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
         m_pMessage->data.raise_state_identity_64.flavor = eFlavor;
-        m_pMessage->data.raise_state_identity_64.old_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_identity_64.old_state, pState, cbState);
+        m_pMessage->data.raise_state_identity_64.old_state_count = count;
+        memcpy(m_pMessage->data.raise_state_identity_64.old_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
         m_pMessage->data.raise_state_reply.flavor = eFlavor;
-        m_pMessage->data.raise_state_reply.new_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_reply.new_state, pState, cbState);
+        m_pMessage->data.raise_state_reply.new_state_count = count;
+        memcpy(m_pMessage->data.raise_state_reply.new_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
         m_pMessage->data.raise_state_reply_64.flavor = eFlavor;
-        m_pMessage->data.raise_state_reply_64.new_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_reply_64.new_state, pState, cbState);
+        m_pMessage->data.raise_state_reply_64.new_state_count = count;
+        memcpy(m_pMessage->data.raise_state_reply_64.new_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
         m_pMessage->data.raise_state_identity_reply.flavor = eFlavor;
-        m_pMessage->data.raise_state_identity_reply.new_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_identity_reply.new_state, pState, cbState);
+        m_pMessage->data.raise_state_identity_reply.new_state_count = count;
+        memcpy(m_pMessage->data.raise_state_identity_reply.new_state, pState, count * sizeof(natural_t));
         break;
 
     case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
         m_pMessage->data.raise_state_identity_reply_64.flavor = eFlavor;
-        m_pMessage->data.raise_state_identity_reply_64.new_state_count = cbState / sizeof(natural_t);
-        memcpy(m_pMessage->data.raise_state_identity_reply_64.new_state, pState, cbState);
+        m_pMessage->data.raise_state_identity_reply_64.new_state_count = count;
+        memcpy(m_pMessage->data.raise_state_identity_reply_64.new_state, pState, count * sizeof(natural_t));
         break;
 
     default:
-        FATAL_ERROR("Unsupported message type: %u", m_pMessage->header.msgh_id);
+        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
     }
 }
 
index 1015757..22ec0a7 100644 (file)
@@ -4,55 +4,97 @@
 
 /*++
 
-
-
 Module Name:
 
-    machexception.cpp
+    machmessage.h
 
 Abstract:
 
     Abstraction over Mach messages used during exception handling.
 
-
-
 --*/
 
 #include <mach/mach.h>
 #include <mach/mach_error.h>
 #include <mach/thread_status.h>
 
+using namespace CorUnix;
+
 #if HAVE_MACH_EXCEPTIONS
 
 #if defined(_AMD64_)
-// Constant (exception behavior modifier) defined only in the 10.5 SDK. This is OR'd with one of the standard
-// behavior codes (EXCEPTION_STATE_IDENTITY etc.) to modify the format of the message sent to use 64-bit
-// exception sub-codes instead of the default 32-bit ones.
-#ifndef MACH_EXCEPTION_CODES
-#define MACH_EXCEPTION_CODES 0x80000000
-#endif
-
-typedef        int64_t                         mach_exception_data_type_t;
-typedef mach_exception_data_type_t     *mach_exception_data_t;
-typedef        mach_exception_data_type_t      mach_exception_code_t;
-typedef        mach_exception_data_type_t      mach_exception_subcode_t;
-
 #define MACH_EH_TYPE(x) mach_##x
 #else
 #define MACH_EH_TYPE(x) x
 #endif // defined(_AMD64_)
 
+// The vast majority of Mach calls we make in this module are critical: we cannot recover from failures of
+// these methods (principally because we're handling hardware exceptions in the context of a single dedicated
+// handler thread). The following macro encapsulates checking the return code from Mach methods and emitting 
+// some useful data and aborting the process on failure.
+#define CHECK_MACH(_msg, machret) do {                                      \
+        if (machret != KERN_SUCCESS)                                        \
+        {                                                                   \
+            char _szError[1024];                                            \
+            sprintf(_szError, "%s: %u: %s", __FUNCTION__, __LINE__, _msg);  \
+            mach_error(_szError, machret);                                  \
+            abort();                                                        \
+        }                                                                   \
+    } while (false)
+
+// This macro terminates the process with some useful debug info as above, but for the general failure points
+// that have nothing to do with Mach.
+#define NONPAL_RETAIL_ASSERT(_msg, ...) do {                                    \
+        printf("%s: %u: " _msg "\n", __FUNCTION__, __LINE__, ## __VA_ARGS__);   \
+        abort();                                                                \
+    } while (false)
+
+#define NONPAL_RETAIL_ASSERTE(_expr) do {                    \
+        if (!(_expr))                                        \
+            NONPAL_RETAIL_ASSERT("ASSERT: %s\n", #_expr);    \
+    } while (false)
+
 #ifdef _DEBUG
 
+#define NONPAL_TRACE_ENABLED getenv("NONPAL_TRACING")
+
+#define NONPAL_ASSERT(_msg, ...) NONPAL_RETAIL_ASSERT(_msg, __VA_ARGS__)
+
+// Assert macro that doesn't rely on the PAL.
+#define NONPAL_ASSERTE(_expr) do {                           \
+        if (!(_expr))                                        \
+            NONPAL_RETAIL_ASSERT("ASSERT: %s\n", #_expr);    \
+    } while (false)
+
 // Debug-only output with printf-style formatting.
 #define NONPAL_TRACE(_format, ...) do {                                                  \
-        if (getenv("NONPAL_TRACING")) printf("NONPAL_TRACE: " _format, ## __VA_ARGS__);  \
+        if (NONPAL_TRACE_ENABLED) printf("NONPAL_TRACE: " _format, ## __VA_ARGS__);  \
     } while (false)
+
 #else // _DEBUG
+
+#define NONPAL_TRACE_ENABLED false 
+#define NONPAL_ASSERT(_msg, ...)
+#define NONPAL_ASSERTE(_expr)
 #define NONPAL_TRACE(_format, ...)
+
 #endif // _DEBUG
 
-struct MachExceptionHandler;
+class MachMessage;
+
+// Contains all the exception and thread state information needed to forward the exception.
+struct MachExceptionInfo
+{
+    exception_type_t ExceptionType;
+    mach_msg_type_number_t SubcodeCount;
+    MACH_EH_TYPE(exception_data_type_t) Subcodes[2];
+    x86_thread_state_t ThreadState;
+    x86_float_state_t FloatState;
+    x86_debug_state_t DebugState;
+
+    MachExceptionInfo(mach_port_t thread, MachMessage& message);
+    void RestoreState(mach_port_t thread);
+};
 
 // Abstraction of a subset of Mach message types. Provides accessors that hide the subtle differences in the
 // message layout of similar message types.
@@ -64,6 +106,7 @@ public:
     enum MessageType
     {
         SET_THREAD_MESSAGE_ID = 1,
+        FORWARD_EXCEPTION_MESSAGE_ID = 2,
         NOTIFY_SEND_ONCE_MESSAGE_ID = 71,
         EXCEPTION_RAISE_MESSAGE_ID = 2401,
         EXCEPTION_RAISE_STATE_MESSAGE_ID = 2402,
@@ -83,19 +126,16 @@ public:
     // ForwardNotification() or ReplyToNotification() to construct a message and sent it.
     MachMessage();
 
-    // Initializes this message from another
-    void InitializeFrom(const MachMessage& source);
-
     // Listen for the next message on the given port and initialize this class with the contents. The message
     // type must match one of the MessageTypes indicated above (or the process will be aborted).
     void Receive(mach_port_t hPort);
 
     // Indicate whether a received message belongs to a particular semantic class.
     bool IsSetThreadRequest();          // Message is a request to set the context of a particular thread
+    bool IsForwardExceptionRequest();   // Message is a request to forward the exception
+    bool IsSendOnceDestroyedNotify();   // Message is a notification that a send-once message was destroyed by the receiver
     bool IsExceptionNotification();     // Message is a notification of an exception
     bool IsExceptionReply();            // Message is a reply to the notification of an exception
-    bool IsSendOnceDestroyedNotify();   // Message is a notification that a send-once message was destroyed by
-                                        // the receiver
 
     // Get properties of a received message header.
     MessageType GetMessageType();       // The message type
@@ -107,6 +147,12 @@ public:
     // from the message and returns the target thread to which the context should be applied.
     thread_act_t GetThreadContext(CONTEXT *pContext);
 
+    // Returns the pal thread instance for the forward exception message
+    CPalThread *GetPalThread();
+
+    // Returns the exception info from the forward exception message
+    MachExceptionInfo *GetExceptionInfo();
+
     // Get properties of the type-specific portion of the message. The following properties are supported by
     // exception notification messages only.
     thread_act_t GetThread();           // Get the faulting thread
@@ -122,22 +168,27 @@ public:
     // message doesn't contain a thread state or the flavor of the state in the message doesn't match, the
     // state will be fetched directly from the target thread instead (which can be computed implicitly for
     // exception messages or passed explicitly for reply messages).
-    size_t GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t hThread = NULL);
+    mach_msg_type_number_t GetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, thread_act_t thread = NULL);
+
+    // Fetch the return code from a reply type message.
+    kern_return_t GetReturnCode();
 
     // Initialize and send a request to set the register context of a particular thread.
-    void SendSetThread(mach_port_t hServerPort, thread_act_t hThread, CONTEXT *pContext);
+    void SendSetThread(mach_port_t hServerPort, CONTEXT *pContext);
+
+    // Initialize and send a request to forward the exception message to the notification thread
+    void SendForwardException(mach_port_t hServerPort, MachExceptionInfo *pExceptionInfo, CPalThread *ppalThread);
 
     // Initialize the message (overwriting any previous content) to represent a forwarded version of the given
     // exception notification message and send that message to the chain-back handler previously registered
     // for the exception type being notified. The new message takes account of the fact that the target
-    // handler may not have requested the same notification behavior or flavor as our handler. Blocks until
-    // the reply is received.
-    void ForwardNotification(CorUnix::MachExceptionHandler *pHandler, MachMessage *pNotification);
+    // handler may not have requested the same notification behavior or flavor as our handler.
+    void ForwardNotification(MachExceptionHandler *pHandler, MachMessage& message);
 
     // Initialize the message (overwriting any previous content) to represent a reply to the given exception
     // notification and send that reply back to the original sender of the notification. This is used when our
     // handler handles the exception rather than forwarding it to a chain-back handler.
-    void ReplyToNotification(MachMessage *pNotification, kern_return_t eResult);
+    void ReplyToNotification(MachMessage& message, kern_return_t eResult);
 
 private:
     // The maximum size in bytes of any Mach message we can send or receive. Calculating an exact size for
@@ -155,6 +206,17 @@ private:
         CONTEXT new_context;
     };
 
+    // Request to forward the exception notification
+    // FORWARD_EXCEPTION_MESSAGE_ID
+    struct forward_exception_request_t
+    {
+        thread_act_t thread;
+        CPalThread *ppalThread;
+        MachExceptionInfo exception_info;
+    };
+
+#pragma pack(4)
+
     // EXCEPTION_RAISE_MESSAGE_ID
     struct exception_raise_notification_t
     {
@@ -164,7 +226,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        integer_t code[2];
+        exception_data_type_t code[2];
     };
 
     // EXCEPTION_RAISE_REPLY_MESSAGE_ID
@@ -183,7 +245,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        int64_t code[2];
+        mach_exception_data_type_t code[2];
     };
 
     // EXCEPTION_RAISE_REPLY_64_MESSAGE_ID
@@ -199,7 +261,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        integer_t code[2];
+        exception_data_type_t code[2];
         thread_state_flavor_t flavor;
         mach_msg_type_number_t old_state_count;
         natural_t old_state[THREAD_STATE_MAX];
@@ -221,7 +283,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        int64_t code[2];
+        mach_exception_data_type_t code[2];
         thread_state_flavor_t flavor;
         mach_msg_type_number_t old_state_count;
         natural_t old_state[THREAD_STATE_MAX];
@@ -246,7 +308,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        integer_t code[2];
+        exception_data_type_t code[2];
         thread_state_flavor_t flavor;
         mach_msg_type_number_t old_state_count;
         natural_t old_state[THREAD_STATE_MAX];
@@ -271,7 +333,7 @@ private:
         NDR_record_t ndr;
         exception_type_t exception;
         mach_msg_type_number_t code_count;
-        int64_t code[2];
+        mach_exception_data_type_t code[2];
         thread_state_flavor_t flavor;
         mach_msg_type_number_t old_state_count;
         natural_t old_state[THREAD_STATE_MAX];
@@ -287,6 +349,8 @@ private:
         natural_t new_state[THREAD_STATE_MAX];
     };
 
+#pragma pack()
+
     // All the above messages are sent with a standard Mach header prepended. This structure unifies the
     // message formats.
     struct mach_message_t
@@ -295,6 +359,7 @@ private:
         union
         {
             set_thread_request_t                                set_thread;
+            forward_exception_request_t                         forward_exception;
             exception_raise_notification_t                      raise;
             exception_raise_state_notification_t                raise_state;
             exception_raise_state_identity_notification_t       raise_state_identity;
@@ -337,9 +402,6 @@ private:
     // Transform a Mach message ID for an exception notification into the corresponding ID for the reply.
     mach_msg_id_t MapNotificationToReplyType(mach_msg_id_t eNotificationType);
 
-    // Fetch the return code from a reply type message.
-    kern_return_t GetReturnCode();
-
     // The following methods initialize fields on the message prior to transmission. Each is valid for either
     // notification, replies or both. If a particular setter is defined for replies, say, then it will be a
     // no-op for any replies which don't contain that field. This makes transforming between notifications and
@@ -347,7 +409,7 @@ private:
     // those operations that make sense will do any work).
 
     // Defined for notifications:
-    void SetThread(thread_act_t hThread);
+    void SetThread(thread_act_t thread);
     void SetException(exception_type_t eException);
     void SetExceptionCodeCount(int cCodes);
     void SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_t) iCode);
@@ -356,7 +418,7 @@ private:
     void SetReturnCode(kern_return_t eReturnCode);
 
     // Defined for both notifications and replies.
-    void SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, size_t cbState);
+    void SetThreadState(thread_state_flavor_t eFlavor, thread_state_t pState, mach_msg_type_number_t count);
 
     // Maximally sized buffer for the message to be received into or transmitted out of this class.
     unsigned char   m_rgMessageBuffer[kcbMaxMessageSize];
index ab90031..abaa025 100644 (file)
@@ -123,24 +123,6 @@ namespace CorUnix
         
 #ifdef FEATURE_PAL_SXS
 #if HAVE_MACH_EXCEPTIONS
-    // Structure used to record all Mach exception handlers registered on a given thread at a specific point
-    // in time.
-    struct CThreadMachExceptionHandlerNode
-    {
-        // Maximum number of exception ports we hook.  Must be the count
-        // of all bits set in the exception masks defined in machexception.h.
-        static const int s_nPortsMax = 6;
-
-        // Saved exception ports, exactly as returned by
-        // thread_swap_exception_ports.
-        int m_nPorts;
-        exception_mask_t m_masks[s_nPortsMax];
-        exception_handler_t m_handlers[s_nPortsMax];
-        exception_behavior_t m_behaviors[s_nPortsMax];
-        thread_state_flavor_t m_flavors[s_nPortsMax];
-        
-        CThreadMachExceptionHandlerNode() : m_nPorts(-1) {}
-    };
 
     // Structure used to return data about a single handler to a caller.
     struct MachExceptionHandler
@@ -151,22 +133,26 @@ namespace CorUnix
         thread_state_flavor_t m_flavor;
     };
 
-    // Class abstracting previousy registered Mach exception handlers for a thread.
-    class CThreadMachExceptionHandlers
+    // Class abstracting previously registered Mach exception handlers for a thread.
+    struct CThreadMachExceptionHandlers
     {
     public:
-        // Returns a pointer to the handler node that should be initialized next. The first time this is
-        // called for a thread the bottom node will be returned. Thereafter the top node will be returned.
-        // Also returns the Mach exception port that should be registered.
-        CThreadMachExceptionHandlerNode *GetNodeForInitialization();
-
-        // Returns a pointer to the handler node for cleanup. This will always be the bottom node. This isn't
-        // really the right algorithm (because there isn't one). There are lots of reasonable scenarios where
-        // we will break chaining by removing our handler from a thread (by registering two handler ports we
-        // can support a lot more chaining scenarios but we can't pull the same sort of trick when
-        // unregistering, in particular we have two sets of chain back handlers and no way to reach into other
-        // components and alter what their chain-back information is).
-        CThreadMachExceptionHandlerNode *GetNodeForCleanup() { return &m_node; }
+        // Maximum number of exception ports we hook.  Must be the count
+        // of all bits set in the exception masks defined in machexception.h.
+        static const int s_nPortsMax = 6;
+
+        // Saved exception ports, exactly as returned by
+        // thread_swap_exception_ports.
+        mach_msg_type_number_t m_nPorts;
+        exception_mask_t m_masks[s_nPortsMax];
+        exception_handler_t m_handlers[s_nPortsMax];
+        exception_behavior_t m_behaviors[s_nPortsMax];
+        thread_state_flavor_t m_flavors[s_nPortsMax];
+        
+        CThreadMachExceptionHandlers() : 
+            m_nPorts(-1)
+        {
+        }
 
         // Get handler details for a given type of exception. If successful the structure pointed at by
         // pHandler is filled in and true is returned. Otherwise false is returned.
@@ -175,9 +161,7 @@ namespace CorUnix
     private:
         // Look for a handler for the given exception within the given handler node. Return its index if
         // successful or -1 otherwise.
-        int GetIndexOfHandler(exception_mask_t bmExceptionMask, CThreadMachExceptionHandlerNode *pNode);
-
-        CThreadMachExceptionHandlerNode m_node;
+        int GetIndexOfHandler(exception_mask_t bmExceptionMask);
     };
 #endif // HAVE_MACH_EXCEPTIONS
 #endif // FEATURE_PAL_SXS
index bfd517b..07f6c8b 100644 (file)
@@ -2738,51 +2738,41 @@ PAL_InjectActivation(
 
 extern mach_port_t s_ExceptionPort;
 
-// Returns a pointer to the handler node that should be initialized next. The first time this is called for a
-// thread the bottom node will be returned. Thereafter the top node will be returned. Also returns the Mach
-// exception port that should be registered.
-CorUnix::CThreadMachExceptionHandlerNode *CorUnix::CThreadMachExceptionHandlers::GetNodeForInitialization()
-{
-    return &m_node;
-}
-
 // Get handler details for a given type of exception. If successful the structure pointed at by pHandler is
 // filled in and true is returned. Otherwise false is returned.
 bool CorUnix::CThreadMachExceptionHandlers::GetHandler(exception_type_t eException, CorUnix::MachExceptionHandler *pHandler)
 {
     exception_mask_t bmExceptionMask = (1 << eException);
-    CThreadMachExceptionHandlerNode *pNode = &m_node;
-    int idxHandler = GetIndexOfHandler(bmExceptionMask, pNode);
+    int idxHandler = GetIndexOfHandler(bmExceptionMask);
 
     // Did we find a handler?
     if (idxHandler == -1)
         return false;
 
     // Found one, so initialize the output structure with the details.
-    pHandler->m_mask = pNode->m_masks[idxHandler];
-    pHandler->m_handler = pNode->m_handlers[idxHandler];
-    pHandler->m_behavior = pNode->m_behaviors[idxHandler];
-    pHandler->m_flavor = pNode->m_flavors[idxHandler];
+    pHandler->m_mask = m_masks[idxHandler];
+    pHandler->m_handler = m_handlers[idxHandler];
+    pHandler->m_behavior = m_behaviors[idxHandler];
+    pHandler->m_flavor = m_flavors[idxHandler];
 
     return true;
 }
 
 // Look for a handler for the given exception within the given handler node. Return its index if successful or
 // -1 otherwise.
-int CorUnix::CThreadMachExceptionHandlers::GetIndexOfHandler(exception_mask_t bmExceptionMask,
-                                                             CorUnix::CThreadMachExceptionHandlerNode *pNode)
+int CorUnix::CThreadMachExceptionHandlers::GetIndexOfHandler(exception_mask_t bmExceptionMask)
 {
     // Check all handler entries for one handling the exception mask.
-    for (int i = 0; i < pNode->m_nPorts; i++)
+    for (mach_msg_type_number_t i = 0; i < m_nPorts; i++)
     {
-        if (pNode->m_masks[i] & bmExceptionMask &&      // Entry covers this exception type
-            pNode->m_handlers[i] != MACH_PORT_NULL &&   // And the handler isn't null
-            pNode->m_handlers[i] != s_ExceptionPort)    // And the handler isn't ourselves
-        {
+        // Entry covers this exception type and the handler isn't null
+        if (m_masks[i] & bmExceptionMask && m_handlers[i] != MACH_PORT_NULL)
+        { 
+            _ASSERTE(m_handlers[i] != s_ExceptionPort);
+
             // One more check; has the target handler port become dead?
             mach_port_type_t ePortType;
-            if (mach_port_type(mach_task_self(), pNode->m_handlers[i], &ePortType) == KERN_SUCCESS &&
-                !(ePortType & MACH_PORT_TYPE_DEAD_NAME))
+            if (mach_port_type(mach_task_self(), m_handlers[i], &ePortType) == KERN_SUCCESS && !(ePortType & MACH_PORT_TYPE_DEAD_NAME))
             {
                 // Got a matching entry.
                 return i;
index 1b3ec0a..8aa7a0f 100644 (file)
@@ -30,10 +30,10 @@ int DllTest1()
 
     PAL_TRY(VOID*, unused, NULL)
     {
-        volatile int* p = (volatile int *)0x11; /* invalid pointer */
+        volatile int* p = (volatile int *)0x11; // Invalid pointer
 
-        bTry = TRUE;    /* indicate we hit the PAL_TRY block */
-        *p = 13;        /* causes an access violation exception */
+        bTry = TRUE;                            // Indicate we hit the PAL_TRY block
+        *p = 1;                                 // Causes an access violation exception
 
         Fail("ERROR: code was executed after the access violation.\n");
     }
@@ -44,7 +44,13 @@ int DllTest1()
             Fail("ERROR: PAL_EXCEPT was hit without PAL_TRY being hit.\n");
         }
 
-        bExcept = TRUE; /* indicate we hit the PAL_EXCEPT block */
+        // Validate that the faulting address is correct; the contents of "p" (0x11).
+        if (ex.ExceptionRecord.ExceptionInformation[1] != 0x11)
+        {
+            Fail("ERROR: PAL_EXCEPT ExceptionInformation[1] != 0x11\n");
+        }
+
+        bExcept = TRUE;                         // Indicate we hit the PAL_EXCEPT block 
     }
     PAL_ENDTRY;
 
@@ -58,13 +64,12 @@ int DllTest1()
         Trace("ERROR: the code in the PAL_EXCEPT block was not executed.\n");
     }
 
-    /* did we hit all the code blocks? */
+    // Did we hit all the code blocks? 
     if(!bTry || !bExcept)
     {
         Fail("DllTest1 FAILED\n");
     }
 
     Trace("DLLTest1 PASSED\n");
-
     return PASS;
 }
index 5d3051d..fe5c8e0 100644 (file)
@@ -30,10 +30,10 @@ int DllTest2()
 
     PAL_TRY(VOID*, unused, NULL)
     {
-        volatile int* p = (volatile int *)0x22; /* invalid pointer */
+        volatile int* p = (volatile int *)0x22; // Invalid pointer
 
-        bTry = TRUE;    /* indicate we hit the PAL_TRY block */
-        *p = 13;        /* causes an access violation exception */
+        bTry = TRUE;                            // Indicate we hit the PAL_TRY block
+        *p = 2;                                 // Causes an access violation exception
 
         Fail("ERROR: code was executed after the access violation.\n");
     }
@@ -44,7 +44,13 @@ int DllTest2()
             Fail("ERROR: PAL_EXCEPT was hit without PAL_TRY being hit.\n");
         }
 
-        bExcept = TRUE; /* indicate we hit the PAL_EXCEPT block */
+        // Validate that the faulting address is correct; the contents of "p" (0x22).
+        if (ex.ExceptionRecord.ExceptionInformation[1] != 0x22)
+        {
+            Fail("ERROR: PAL_EXCEPT ExceptionInformation[1] != 0x22\n");
+        }
+
+        bExcept = TRUE;                         // Indicate we hit the PAL_EXCEPT block
     }
     PAL_ENDTRY;
 
@@ -58,13 +64,12 @@ int DllTest2()
         Trace("ERROR: the code in the PAL_EXCEPT block was not executed.\n");
     }
 
-    /* did we hit all the code blocks? */
+    // Did we hit all the code blocks?
     if(!bTry || !bExcept)
     {
         Fail("DllTest2 FAILED\n");
     }
 
     Trace("DLLTest2 PASSED\n");
-
     return PASS;
 }
index 3a4aa55..19a2412 100644 (file)
 **
 **===================================================================*/
 
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+enum
+{
+    PASS = 0,
+    FAIL = 1
+};
+
 extern "C" int InitializeDllTest1();
 extern "C" int InitializeDllTest2();
 extern "C" int DllTest1();
 extern "C" int DllTest2();
 
-int main(int argc, char *argv[])
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
+
+bool bSignal = false;
+bool bCatch = false;
+bool bHandler = false;
+
+void sigsegv_handler(int code, siginfo_t *siginfo, void *context)
+{
+    printf("pal_sxs test1: signal handler called\n");
+    bHandler = true;                                // Mark that the signal handler was executed
+
+    if (!bSignal)
+    {
+        printf("ERROR: executed signal handler NOT from try/catch\n");
+        _exit(FAIL);
+    }
+
+    // Validate that the faulting address is correct; the contents of "p" (0x22).
+    if (siginfo->si_addr != (void *)0x33)
+    {
+        printf("ERROR: signal handler faulting address != 0x33\n");
+        _exit(FAIL);
+    }
+
+    // Unmask signal so we can receive it again
+    sigset_t signal_set;
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGSEGV);
+    if (-1 == sigprocmask(SIG_UNBLOCK, &signal_set, NULL))
+    {
+        printf("ERROR: sigprocmask failed; error is %d\n", errno);
+        _exit(FAIL);
+    } 
+
+    printf("Signal chaining PASSED\n");
+    _exit(PASS);
+}
+
+#endif
+
+int __cdecl main(int argc, char *argv[])
 {
 #if !defined(__FreeBSD__) && !defined(__NetBSD__)
+    struct sigaction newAction;
+    struct sigaction oldAction;
+    newAction.sa_flags = SA_SIGINFO | SA_RESTART;
+    newAction.sa_handler = NULL;
+    newAction.sa_sigaction = sigsegv_handler;
+    sigemptyset(&newAction.sa_mask);
+
+    if (-1 == sigaction(SIGSEGV, &newAction, &oldAction))
+    {
+        printf("ERROR: sigaction failed; error is %d\n", errno);
+        return FAIL;
+    }
+
+    printf("PAL_SXS test1 SIGSEGV handler %p\n", oldAction.sa_sigaction);
+
     if (0 != InitializeDllTest1())
     {
-        return 1;
+        return FAIL;
     }
 
     if (0 != InitializeDllTest2())
     {
-        return 1;
+        return FAIL;
     }
 
+    // Test catching exceptions in other PAL instances
     DllTest2();
     DllTest1();
     DllTest2();
+
+    if (bHandler)
+    {
+        printf("ERROR: signal handler called by PAL sxs tests\n");
+        return FAIL;
+    }
+
+    printf("Starting PAL_SXS test1 signal chaining\n");
+
+    bSignal = true;
+
+    volatile int* p = (volatile int *)0x33; // Invalid pointer
+    *p = 3;                                 // Causes an access violation exception
+
+    printf("ERROR: code was executed after the access violation.\n");
+    return FAIL;
 #endif
-    return 0;
+    return PASS;
 }