| // |
| // 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 |