//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

/*++

Module Name:

    machmessage.cpp

Abstract:

    Abstraction over Mach messages used during exception handling.

--*/

#include "config.h"
#include "pal/dbgmsg.h"
#include "pal/malloc.hpp"
#include "pal/thread.hpp"
#include "machmessage.h"

#if HAVE_MACH_EXCEPTIONS

// 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()
{
    m_fPortsOwned = false;
    ResetMessage();
}

// 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)
{
    kern_return_t machret;

    // Erase any stale data.
    ResetMessage();

    // Pull the next Mach message into the buffer.
    machret = mach_msg((mach_msg_header_t*)m_rgMessageBuffer,
                       MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_NOTIFY,
                       0,
                       kcbMaxMessageSize,
                       hPort,
                       MACH_MSG_TIMEOUT_NONE,
                       MACH_PORT_NULL);
    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:
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
    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:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }

    m_fPortsOwned = true;
}

// Indicates whether the message is a request to set the context of a thread.
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()
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
    case EXCEPTION_RAISE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return true;
    default:
        return false;
    }
}

// Indicates whether the message is a reply to a notification of an exception.
bool MachMessage::IsExceptionReply()
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        return true;
    default:
        return false;
    }
}

// Returns the type code for a received message.
MachMessage::MessageType MachMessage::GetMessageType()
{
    return (MessageType)m_pMessage->header.msgh_id;
}

// Returns a textual form of the type of a received message. Useful for logging.
const char *MachMessage::GetMessageTypeName()
{
    switch (GetMessageType())
    {
    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:
        return "EXCEPTION_RAISE_REPLY";
    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE";
    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_REPLY";
    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_IDENTITY";
    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_IDENTITY_REPLY";
    case EXCEPTION_RAISE_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_64";
    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_REPLY_64";
    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_64";
    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_REPLY_64";
    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_IDENTITY_64";
    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        return "EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64";
    case NOTIFY_SEND_ONCE_MESSAGE_ID:
        return "NOTIFY_SEND_ONCE";
    default:
        return "<unknown message type>";
    }
}

// Returns the destination port (i.e. the port we listened on to receive this message).
mach_port_t MachMessage::GetLocalPort()
{
    return m_pMessage->header.msgh_local_port;
}

// Returns the source port (the port sending the message) unless no reply is expected, in which case
// MACH_PORT_NULL is returned instead.
mach_port_t MachMessage::GetRemotePort()
{
    return m_pMessage->header.msgh_remote_port;
}

// Do the work of getting ports from the message.
//  * fCalculate -- calculate the thread port if the message did not contain it.
//  * fValidate  -- failfast if the message was not one expected to have a (calculable) thread port.
void MachMessage::GetPorts(bool fCalculate, bool fValidThread)
{
    switch (m_pMessage->header.msgh_id)
    {
    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;
        m_hTask = m_pMessage->data.raise.task_port.name;
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_hThread = m_pMessage->data.raise_64.thread_port.name;
        m_hTask = m_pMessage->data.raise_64.task_port.name;
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        if (fCalculate && m_hThread == MACH_PORT_NULL)
        {
            // This is a tricky case since the message itself doesn't contain the target thread.
            m_hThread = GetThreadFromState(m_pMessage->data.raise_state.flavor,
                                           m_pMessage->data.raise_state.old_state);
        }
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        if (fCalculate && m_hThread == MACH_PORT_NULL)
        {
            // This is a tricky case since the message itself doesn't contain the target thread.
            m_hThread = GetThreadFromState(m_pMessage->data.raise_state_64.flavor,
                                           m_pMessage->data.raise_state_64.old_state);
        }
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_hThread = m_pMessage->data.raise_state_identity.thread_port.name;
        m_hTask = m_pMessage->data.raise_state_identity.task_port.name;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_hThread = m_pMessage->data.raise_state_identity_64.thread_port.name;
        m_hTask = m_pMessage->data.raise_state_identity_64.task_port.name;
        break;

    default:
        if (fValidThread)
        {
            NONPAL_RETAIL_ASSERT("Can only get thread from notification message.");
        }
        break;
    }
}

// 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)
{
    NONPAL_ASSERTE(IsSetThreadRequest());

    memcpy(pContext, &m_pMessage->data.set_thread.new_context, sizeof(CONTEXT));
    m_hThread = m_pMessage->data.set_thread.thread;
    return m_hThread;
}

// Get the target thread for an exception notification message.
thread_act_t MachMessage::GetThread()
{
    GetPorts(true /* fCalculate */, true /* fValidThread */);
    return m_hThread;
}

// Get the exception type for an exception notification message.
exception_type_t MachMessage::GetException()
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        return m_pMessage->data.raise.exception;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        return m_pMessage->data.raise_64.exception;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        return m_pMessage->data.raise_state.exception;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_64.exception;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity.exception;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity_64.exception;

    default:
        NONPAL_RETAIL_ASSERT("Can only get exception from notification message.");
    }
}

// Get the count of sub-codes for an exception notification message.
int MachMessage::GetExceptionCodeCount()
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        return m_pMessage->data.raise.code_count;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        return m_pMessage->data.raise_64.code_count;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        return m_pMessage->data.raise_state.code_count;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_64.code_count;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity.code_count;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity_64.code_count;

    default:
        NONPAL_RETAIL_ASSERT("Can only get exception code count from notification message.");
    }
}

// Get the exception sub-code at the specified zero-based index for an exception notification message.
MACH_EH_TYPE(exception_data_type_t) MachMessage::GetExceptionCode(int iIndex)
{
    if (iIndex < 0 || iIndex >= GetExceptionCodeCount())
    {
        NONPAL_RETAIL_ASSERT("GetExceptionCode() index out of range.");
    }

    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        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 (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 (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:
        NONPAL_RETAIL_ASSERT("Can only get exception code from notification message.");
    }
}

// Fetch the thread state flavor from a notification or reply message (return THREAD_STATE_NONE for the
// messages that don't contain a thread state).
thread_state_flavor_t MachMessage::GetThreadStateFlavor()
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        return THREAD_STATE_NONE;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        return m_pMessage->data.raise_state.flavor;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_64.flavor;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity.flavor;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity_64.flavor;

    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
        return m_pMessage->data.raise_state_reply.flavor;

    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_reply_64.flavor;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity_reply.flavor;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        return m_pMessage->data.raise_state_identity_reply_64.flavor;

    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }
}

// Get the thread state with the given flavor from the exception or exception reply message. If the 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).
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;

    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        // No state in the message, fall through to get it directly from the thread.
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_64.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_identity.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_identity_64.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_reply.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_reply_64.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_identity_reply.flavor == eFlavor)
        {
            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;
    }

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
    {
        // There's a state in the message, but we need to check that the flavor matches what the caller's
        // after (if not we'll fall through and get the correct flavor below).
        if (m_pMessage->data.raise_state_identity_reply_64.flavor == eFlavor)
        {
            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:
        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.
    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;

    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, CONTEXT *pContext)
{
    kern_return_t machret;

    // Set the message type.
    m_pMessage->header.msgh_id = SET_THREAD_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.set_thread.thread = mach_thread_self();
    memcpy(&m_pMessage->data.set_thread.new_context, pContext, sizeof(CONTEXT));

    // 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. (This may not finish executing; nothing is needed to be freed here.)
    ResetMessage();
}

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;

    // Set the message type.
    m_pMessage->header.msgh_id = MapBehaviorToNotificationType(pHandler->m_behavior);

    // Initialize the fields that don't need any further input (this depends on the message type having been
    // set above).
    InitFixedFields();

    // Copy data from the incoming message. Use the getter and setter abstractions to simplify the act that
    // 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(message.GetThread());
    SetException(message.GetException());

    int cCodes = message.GetExceptionCodeCount();
    SetExceptionCodeCount(cCodes);
    for (int i = 0; i < cCodes; i++)
        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 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_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).
    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();
}

// 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& message, kern_return_t eResult)
{
    kern_return_t machret;

    // Set the message type.
    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).
    InitFixedFields();

    SetReturnCode(eResult);

    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 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 = 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).
    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();
}

// Re-initializes this data structure (to the same state as default construction, containing no message).
void MachMessage::ResetMessage()
{
    // Clean up ports if we own them.
    if (m_fPortsOwned)
    {
        kern_return_t machret;

        GetPorts(false /* fCalculate */, false /* fValidThread */);
        if (m_hThread != MACH_PORT_NULL)
        {
            machret = mach_port_deallocate(mach_task_self(), 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);
            CHECK_MACH("mach_port_deallocate(m_hTask)", machret);
        }
    }

#ifdef _DEBUG
    memset(this, 0xcc, sizeof(*this));
#endif

    m_pMessage = (mach_message_t*)m_rgMessageBuffer;
    m_hThread = MACH_PORT_NULL;
    m_hTask = MACH_PORT_NULL;
    m_fPortsOwned = false;
}

// Initialize those fields of a message that are invariant. This method expects that the msgh_id field has
// been filled in prior to the call so it can determine which non-header fields to initialize.
void MachMessage::InitFixedFields()
{
    switch (m_pMessage->header.msgh_id)
    {
    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;
        m_pMessage->data.raise.task_port.name = mach_task_self();
        m_pMessage->data.raise.task_port.pad1 = 0;
        m_pMessage->data.raise.task_port.pad2 = 0;
        m_pMessage->data.raise.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
        m_pMessage->data.raise.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
        m_hTask = mach_task_self();
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_pMessage->data.raise_64.msgh_body.msgh_descriptor_count = 0;
        m_pMessage->data.raise_64.ndr = NDR_record;
        m_pMessage->data.raise_64.task_port.name = mach_task_self();
        m_pMessage->data.raise_64.task_port.pad1 = 0;
        m_pMessage->data.raise_64.task_port.pad2 = 0;
        m_pMessage->data.raise_64.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
        m_pMessage->data.raise_64.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
        m_hTask = mach_task_self();
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->data.raise_state.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        m_pMessage->data.raise_state_64.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity.msgh_body.msgh_descriptor_count = 0;
        m_pMessage->data.raise_state_identity.ndr = NDR_record;
        m_pMessage->data.raise_state_identity.task_port.name = mach_task_self();
        m_pMessage->data.raise_state_identity.task_port.pad1 = 0;
        m_pMessage->data.raise_state_identity.task_port.pad2 = 0;
        m_pMessage->data.raise_state_identity.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
        m_pMessage->data.raise_state_identity.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
        m_hTask = mach_task_self();
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_64.msgh_body.msgh_descriptor_count = 0;
        m_pMessage->data.raise_state_identity_64.ndr = NDR_record;
        m_pMessage->data.raise_state_identity_64.task_port.name = mach_task_self();
        m_pMessage->data.raise_state_identity_64.task_port.pad1 = 0;
        m_pMessage->data.raise_state_identity_64.task_port.pad2 = 0;
        m_pMessage->data.raise_state_identity_64.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
        m_pMessage->data.raise_state_identity_64.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
        m_hTask = mach_task_self();
        break;

    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_reply.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_reply_64.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_state_reply.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_reply_64.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_reply.ndr = NDR_record;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_reply_64.ndr = NDR_record;
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
    }

    m_pMessage->header.msgh_reserved = 0;

    if (m_hTask)
    {
        kern_return_t machret;
        // Addref the task, because the receiver will expect it to own it. (or, if we
        // free it unsent, we'll expect to deallocate it).
        machret = mach_port_mod_refs(mach_task_self(), m_hTask, MACH_PORT_RIGHT_SEND, 1);
    }
}

// Initialize the size field of the message header (msgh_size) based on the message type and other fields.
// This should be called after all other fields have been initialized.
void MachMessage::InitMessageSize()
{
    // Note that in particular the kernel is very particular about the size of messages with embedded thread
    // states. The size of the message must reflect the exact size of the state flavor contained, not the
    // maximum size of a thread state that the message format implies.

    switch (m_pMessage->header.msgh_id)
    {
    case SET_THREAD_MESSAGE_ID:
        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;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_notification_64_t);
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_notification_t, old_state) +
            (m_pMessage->data.raise_state.old_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_notification_64_t, old_state) +
            (m_pMessage->data.raise_state_64.old_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_identity_notification_t, old_state) +
            (m_pMessage->data.raise_state_identity.old_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_identity_notification_64_t, old_state) +
            (m_pMessage->data.raise_state_identity_64.old_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_reply_t);
        break;

    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) + sizeof(exception_raise_reply_64_t);
        break;

    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_reply_t, new_state) +
            (m_pMessage->data.raise_state_reply.new_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_reply_64_t, new_state) +
            (m_pMessage->data.raise_state_reply_64.new_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_identity_reply_t, new_state) +
            (m_pMessage->data.raise_state_identity_reply.new_state_count * sizeof(natural_t));
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        m_pMessage->header.msgh_size = sizeof(mach_msg_header_t) +
            offsetof(exception_raise_state_identity_reply_64_t, new_state) +
            (m_pMessage->data.raise_state_identity_reply_64.new_state_count * sizeof(natural_t));
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unhandled message type: %u", m_pMessage->header.msgh_id);
    }
}

// Given a thread's register context, locate and return the Mach port representing that thread. Only the
// x86_THREAD_STATE and x86_THREAD_STATE32 state flavors are supported for 32-bit.
thread_act_t MachMessage::GetThreadFromState(thread_state_flavor_t eFlavor, thread_state_t pState)
{
    SIZE_T targetSP;

    // Determine SP from the state provided based on its flavor (this algorithm only works with SP, so
    // flavors that don't report this register can't be used). However, hosts that use RAISE_STATE and a
    // flavor of state that don't contain SP should be very, very rare indeed (it's hard to imagine many
    // useful exception handlers that receive neither the exception thread or the general registers of that
    // thread).
    switch (eFlavor)
    {
#ifdef _X86_
    case x86_THREAD_STATE:
        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;
        break;

    case x86_THREAD_STATE64:
        targetSP = ((x86_thread_state64_t*)pState)->__rsp;
        break;
#else
#error Unexpected architecture.
#endif
    default:
        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);
    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.
        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 (threadState.uts.ts32.esp == targetSP)
#elif defined(_AMD64_)
            if (threadState.uts.ts64.__rsp == targetSP)
#else
#error Unexpected architecture.
#endif
            {
                thread_act_t thread = pThreads[i];

                // Increment the refcount; the thread is a "send" right.
                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));
                CHECK_MACH("vm_deallocate()", machret);

                // Return the thread we found.
                return thread;
            }
        }
    }

    // If we got here no thread matched. That shouldn't be possible.
    NONPAL_RETAIL_ASSERT("Failed to locate thread from state.");
}

// Transform a exception handler behavior type into the corresponding Mach message ID for the notification.
mach_msg_id_t MachMessage::MapBehaviorToNotificationType(exception_behavior_t eBehavior)
{
    switch ((uint)eBehavior)
    {
    case EXCEPTION_DEFAULT:
        return EXCEPTION_RAISE_MESSAGE_ID;
    case EXCEPTION_STATE:
        return EXCEPTION_RAISE_STATE_MESSAGE_ID;
    case EXCEPTION_STATE_IDENTITY:
        return EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID;
    case MACH_EXCEPTION_CODES|EXCEPTION_DEFAULT:
        return EXCEPTION_RAISE_64_MESSAGE_ID;
    case MACH_EXCEPTION_CODES|EXCEPTION_STATE:
        return EXCEPTION_RAISE_STATE_64_MESSAGE_ID;
    case MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY:
        return EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID;
    default:
        NONPAL_RETAIL_ASSERT("Unsupported exception behavior type: %u", eBehavior);
    }
}

// Transform a Mach message ID for an exception notification into the corresponding ID for the reply.
mach_msg_id_t MachMessage::MapNotificationToReplyType(mach_msg_id_t eNotificationType)
{
    switch (eNotificationType)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        return EXCEPTION_RAISE_REPLY_MESSAGE_ID;
    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        return EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID;
    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        return EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID;
    case EXCEPTION_RAISE_64_MESSAGE_ID:
        return EXCEPTION_RAISE_REPLY_64_MESSAGE_ID;
    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        return EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID;
    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        return EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID;
    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", eNotificationType);
    }
}

// Set faulting thread in an exception notification message.
void MachMessage::SetThread(thread_act_t thread)
{
    bool fSet = false;

    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        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;
        m_pMessage->data.raise.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
        fSet = true;
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        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;
        m_pMessage->data.raise_64.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
        fSet = true;
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        // No thread field in RAISE_STATE messages.
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        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;
        m_pMessage->data.raise_state_identity.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
        fSet = true;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        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;
        m_pMessage->data.raise_state_identity_64.thread_port.type = MACH_MSG_PORT_DESCRIPTOR;
        fSet = true;
        break;

    default:
        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(), thread, MACH_PORT_RIGHT_SEND, 1);
    }
}

// Set exception type in an exception notification message.
void MachMessage::SetException(exception_type_t eException)
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        m_pMessage->data.raise.exception = eException;
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_pMessage->data.raise_64.exception = eException;
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->data.raise_state.exception = eException;
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        m_pMessage->data.raise_state_64.exception = eException;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity.exception = eException;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_64.exception = eException;
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }
}

// Set exception sub-code count in an exception notification message.
void MachMessage::SetExceptionCodeCount(int cCodes)
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        m_pMessage->data.raise.code_count = cCodes;
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_pMessage->data.raise_64.code_count = cCodes;
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->data.raise_state.code_count = cCodes;
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        m_pMessage->data.raise_state_64.code_count = cCodes;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity.code_count = cCodes;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_64.code_count = cCodes;
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }
}

// Set exception sub-code in an exception notification message.
void MachMessage::SetExceptionCode(int iIndex, MACH_EH_TYPE(exception_data_type_t) iCode)
{
    if (iIndex < 0 || iIndex > 1)
        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.

    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
        m_pMessage->data.raise.code[iIndex] = (int)iCode;
        break;

    case EXCEPTION_RAISE_64_MESSAGE_ID:
        m_pMessage->data.raise_64.code[iIndex] = iCode;
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->data.raise_state.code[iIndex] = (int)iCode;
        break;

    case EXCEPTION_RAISE_STATE_64_MESSAGE_ID:
        m_pMessage->data.raise_state_64.code[iIndex] = iCode;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity.code[iIndex] = (int)iCode;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_64.code[iIndex] = iCode;
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }
}

// Set return code in a reply message.
void MachMessage::SetReturnCode(kern_return_t eReturnCode)
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_reply.ret = eReturnCode;
        break;

    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_reply_64.ret = eReturnCode;
        break;

    case EXCEPTION_RAISE_STATE_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_state_reply.ret = eReturnCode;
        break;

    case EXCEPTION_RAISE_STATE_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_reply_64.ret = eReturnCode;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_reply.ret = eReturnCode;
        break;

    case EXCEPTION_RAISE_STATE_IDENTITY_REPLY_64_MESSAGE_ID:
        m_pMessage->data.raise_state_identity_reply_64.ret = eReturnCode;
        break;

    default:
        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, mach_msg_type_number_t count)
{
    switch (m_pMessage->header.msgh_id)
    {
    case EXCEPTION_RAISE_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_MESSAGE_ID:
    case EXCEPTION_RAISE_64_MESSAGE_ID:
    case EXCEPTION_RAISE_REPLY_64_MESSAGE_ID:
        // No thread state in RAISE or RAISE_REPLY messages.
        break;

    case EXCEPTION_RAISE_STATE_MESSAGE_ID:
        m_pMessage->data.raise_state.flavor = eFlavor;
        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 = 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 = 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 = 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 = 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 = 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 = 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 = count;
        memcpy(m_pMessage->data.raise_state_identity_reply_64.new_state, pState, count * sizeof(natural_t));
        break;

    default:
        NONPAL_RETAIL_ASSERT("Unsupported message type: %u", m_pMessage->header.msgh_id);
    }
}

#endif // HAVE_MACH_EXCEPTIONS
