xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachException.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- MachException.cpp ---------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 6/18/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "MachException.h"
14061da546Spatrick #include "DNB.h"
15061da546Spatrick #include "DNBError.h"
16061da546Spatrick #include "DNBLog.h"
17061da546Spatrick #include "MachProcess.h"
18061da546Spatrick #include "PThreadMutex.h"
19061da546Spatrick #include "SysSignal.h"
20be691f3bSpatrick #include <cerrno>
21061da546Spatrick #include <sys/ptrace.h>
22061da546Spatrick #include <sys/types.h>
23061da546Spatrick 
24061da546Spatrick // Routine mach_exception_raise
25061da546Spatrick extern "C" kern_return_t
26061da546Spatrick catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread,
27061da546Spatrick                            mach_port_t task, exception_type_t exception,
28061da546Spatrick                            mach_exception_data_t code,
29061da546Spatrick                            mach_msg_type_number_t codeCnt);
30061da546Spatrick 
31061da546Spatrick extern "C" kern_return_t catch_mach_exception_raise_state(
32061da546Spatrick     mach_port_t exception_port, exception_type_t exception,
33061da546Spatrick     const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
34061da546Spatrick     int *flavor, const thread_state_t old_state,
35061da546Spatrick     mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
36061da546Spatrick     mach_msg_type_number_t *new_stateCnt);
37061da546Spatrick 
38061da546Spatrick // Routine mach_exception_raise_state_identity
39061da546Spatrick extern "C" kern_return_t catch_mach_exception_raise_state_identity(
40061da546Spatrick     mach_port_t exception_port, mach_port_t thread, mach_port_t task,
41061da546Spatrick     exception_type_t exception, mach_exception_data_t code,
42061da546Spatrick     mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state,
43061da546Spatrick     mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
44061da546Spatrick     mach_msg_type_number_t *new_stateCnt);
45061da546Spatrick 
46061da546Spatrick extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP,
47061da546Spatrick                                      mach_msg_header_t *OutHeadP);
48061da546Spatrick 
49061da546Spatrick // Note: g_message points to the storage allocated to catch the data from
50061da546Spatrick // catching the current exception raise. It's populated when we catch a raised
51061da546Spatrick // exception which can't immediately be replied to.
52061da546Spatrick //
53061da546Spatrick // If it becomes possible to catch exceptions from multiple threads
54061da546Spatrick // simultaneously, accesses to g_message would need to be mutually exclusive.
55061da546Spatrick static MachException::Data *g_message = NULL;
56061da546Spatrick 
catch_mach_exception_raise_state(mach_port_t exc_port,exception_type_t exc_type,const mach_exception_data_t exc_data,mach_msg_type_number_t exc_data_count,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)57061da546Spatrick extern "C" kern_return_t catch_mach_exception_raise_state(
58061da546Spatrick     mach_port_t exc_port, exception_type_t exc_type,
59061da546Spatrick     const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count,
60061da546Spatrick     int *flavor, const thread_state_t old_state,
61061da546Spatrick     mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
62061da546Spatrick     mach_msg_type_number_t *new_stateCnt) {
63061da546Spatrick   if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
64061da546Spatrick     DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data "
65061da546Spatrick                    "= 0x%llx, exc_data_count = %d)",
66061da546Spatrick                    __FUNCTION__, exc_port, exc_type,
67061da546Spatrick                    MachException::Name(exc_type), (uint64_t)exc_data,
68061da546Spatrick                    exc_data_count);
69061da546Spatrick   }
70061da546Spatrick   return KERN_FAILURE;
71061da546Spatrick }
72061da546Spatrick 
catch_mach_exception_raise_state_identity(mach_port_t exc_port,mach_port_t thread_port,mach_port_t task_port,exception_type_t exc_type,mach_exception_data_t exc_data,mach_msg_type_number_t exc_data_count,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)73061da546Spatrick extern "C" kern_return_t catch_mach_exception_raise_state_identity(
74061da546Spatrick     mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port,
75061da546Spatrick     exception_type_t exc_type, mach_exception_data_t exc_data,
76061da546Spatrick     mach_msg_type_number_t exc_data_count, int *flavor,
77061da546Spatrick     thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
78061da546Spatrick     thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
79061da546Spatrick   if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
80061da546Spatrick     DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = "
81061da546Spatrick                    "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, "
82061da546Spatrick                    "0x%llx })",
83061da546Spatrick                    __FUNCTION__, exc_port, thread_port, task_port, exc_type,
84061da546Spatrick                    MachException::Name(exc_type), exc_data_count,
85061da546Spatrick                    (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
86061da546Spatrick                    (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
87061da546Spatrick   }
88061da546Spatrick 
89061da546Spatrick   return KERN_FAILURE;
90061da546Spatrick }
91061da546Spatrick 
92061da546Spatrick extern "C" kern_return_t
catch_mach_exception_raise(mach_port_t exc_port,mach_port_t thread_port,mach_port_t task_port,exception_type_t exc_type,mach_exception_data_t exc_data,mach_msg_type_number_t exc_data_count)93061da546Spatrick catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port,
94061da546Spatrick                            mach_port_t task_port, exception_type_t exc_type,
95061da546Spatrick                            mach_exception_data_t exc_data,
96061da546Spatrick                            mach_msg_type_number_t exc_data_count) {
97061da546Spatrick   if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
98061da546Spatrick     DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = "
99061da546Spatrick                    "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, "
100061da546Spatrick                    "0x%llx })",
101061da546Spatrick                    __FUNCTION__, exc_port, thread_port, task_port, exc_type,
102061da546Spatrick                    MachException::Name(exc_type), exc_data_count,
103061da546Spatrick                    (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
104061da546Spatrick                    (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
105061da546Spatrick   }
106061da546Spatrick   g_message->exc_type = 0;
107061da546Spatrick   g_message->exc_data.clear();
108061da546Spatrick 
109061da546Spatrick   if (task_port == g_message->task_port) {
110061da546Spatrick     g_message->task_port = task_port;
111061da546Spatrick     g_message->thread_port = thread_port;
112061da546Spatrick     g_message->exc_type = exc_type;
113061da546Spatrick     g_message->AppendExceptionData(exc_data, exc_data_count);
114061da546Spatrick     return KERN_SUCCESS;
115061da546Spatrick   } else if (!MachTask::IsValid(g_message->task_port)) {
116061da546Spatrick     // Our original exception port isn't valid anymore check for a SIGTRAP
117061da546Spatrick     if (exc_type == EXC_SOFTWARE && exc_data_count == 2 &&
118061da546Spatrick         exc_data[0] == EXC_SOFT_SIGNAL && exc_data[1] == SIGTRAP) {
119061da546Spatrick       // We got a SIGTRAP which indicates we might have exec'ed and possibly
120061da546Spatrick       // lost our old task port during the exec, so we just need to switch over
121061da546Spatrick       // to using this new task port
122061da546Spatrick       g_message->task_port = task_port;
123061da546Spatrick       g_message->thread_port = thread_port;
124061da546Spatrick       g_message->exc_type = exc_type;
125061da546Spatrick       g_message->AppendExceptionData(exc_data, exc_data_count);
126061da546Spatrick       return KERN_SUCCESS;
127061da546Spatrick     }
128061da546Spatrick   }
129061da546Spatrick   return KERN_FAILURE;
130061da546Spatrick }
131061da546Spatrick 
Dump() const132061da546Spatrick void MachException::Message::Dump() const {
133061da546Spatrick   DNBLogThreadedIf(LOG_EXCEPTIONS, "  exc_msg { bits = 0x%8.8x size = 0x%8.8x "
134061da546Spatrick                                    "remote-port = 0x%8.8x local-port = 0x%8.8x "
135061da546Spatrick                                    "reserved = 0x%8.8x id = 0x%8.8x } ",
136061da546Spatrick                    exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
137061da546Spatrick                    exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
138061da546Spatrick                    exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id);
139061da546Spatrick 
140061da546Spatrick   DNBLogThreadedIf(LOG_EXCEPTIONS, "reply_msg { bits = 0x%8.8x size = 0x%8.8x "
141061da546Spatrick                                    "remote-port = 0x%8.8x local-port = 0x%8.8x "
142061da546Spatrick                                    "reserved = 0x%8.8x id = 0x%8.8x }",
143061da546Spatrick                    reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
144061da546Spatrick                    reply_msg.hdr.msgh_remote_port,
145061da546Spatrick                    reply_msg.hdr.msgh_local_port, reply_msg.hdr.msgh_reserved,
146061da546Spatrick                    reply_msg.hdr.msgh_id);
147061da546Spatrick 
148061da546Spatrick   state.Dump();
149061da546Spatrick }
150061da546Spatrick 
GetStopInfo(struct DNBThreadStopInfo * stop_info) const151061da546Spatrick bool MachException::Data::GetStopInfo(
152061da546Spatrick     struct DNBThreadStopInfo *stop_info) const {
153061da546Spatrick   // Zero out the structure.
154061da546Spatrick   memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
155061da546Spatrick 
156061da546Spatrick   if (exc_type == 0) {
157061da546Spatrick     stop_info->reason = eStopTypeInvalid;
158061da546Spatrick     return true;
159061da546Spatrick   }
160061da546Spatrick 
161061da546Spatrick   // We always stop with a mach exceptions
162061da546Spatrick   stop_info->reason = eStopTypeException;
163061da546Spatrick   // Save the EXC_XXXX exception type
164061da546Spatrick   stop_info->details.exception.type = exc_type;
165061da546Spatrick 
166061da546Spatrick   // Fill in a text description
167061da546Spatrick   const char *exc_name = MachException::Name(exc_type);
168061da546Spatrick   char *desc = stop_info->description;
169061da546Spatrick   const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH;
170061da546Spatrick   if (exc_name)
171061da546Spatrick     desc +=
172061da546Spatrick         snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name);
173061da546Spatrick   else
174061da546Spatrick     desc +=
175061da546Spatrick         snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type);
176061da546Spatrick 
177061da546Spatrick   stop_info->details.exception.data_count = exc_data.size();
178061da546Spatrick 
179061da546Spatrick   int soft_signal = SoftSignal();
180061da546Spatrick   if (soft_signal) {
181061da546Spatrick     if (desc < end_desc) {
182061da546Spatrick       const char *sig_str = SysSignal::Name(soft_signal);
183061da546Spatrick       snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))",
184061da546Spatrick                soft_signal, sig_str ? sig_str : "unknown signal");
185061da546Spatrick     }
186061da546Spatrick   } else {
187061da546Spatrick     // No special disassembly for exception data, just
188061da546Spatrick     size_t idx;
189061da546Spatrick     if (desc < end_desc) {
190061da546Spatrick       desc += snprintf(desc, end_desc - desc, " data[%llu] = {",
191061da546Spatrick                        (uint64_t)stop_info->details.exception.data_count);
192061da546Spatrick 
193061da546Spatrick       for (idx = 0;
194061da546Spatrick            desc < end_desc && idx < stop_info->details.exception.data_count;
195061da546Spatrick            ++idx)
196061da546Spatrick         desc += snprintf(
197061da546Spatrick             desc, end_desc - desc, "0x%llx%c", (uint64_t)exc_data[idx],
198061da546Spatrick             ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
199061da546Spatrick     }
200061da546Spatrick   }
201061da546Spatrick 
202061da546Spatrick   // Copy the exception data
203061da546Spatrick   size_t i;
204061da546Spatrick   for (i = 0; i < stop_info->details.exception.data_count; i++)
205061da546Spatrick     stop_info->details.exception.data[i] = exc_data[i];
206061da546Spatrick 
207061da546Spatrick   return true;
208061da546Spatrick }
209061da546Spatrick 
DumpStopReason() const210061da546Spatrick void MachException::Data::DumpStopReason() const {
211061da546Spatrick   int soft_signal = SoftSignal();
212061da546Spatrick   if (soft_signal) {
213061da546Spatrick     const char *signal_str = SysSignal::Name(soft_signal);
214061da546Spatrick     if (signal_str)
215061da546Spatrick       DNBLog("signal(%s)", signal_str);
216061da546Spatrick     else
217061da546Spatrick       DNBLog("signal(%i)", soft_signal);
218061da546Spatrick     return;
219061da546Spatrick   }
220061da546Spatrick   DNBLog("%s", Name(exc_type));
221061da546Spatrick }
222061da546Spatrick 
Receive(mach_port_t port,mach_msg_option_t options,mach_msg_timeout_t timeout,mach_port_t notify_port)223061da546Spatrick kern_return_t MachException::Message::Receive(mach_port_t port,
224061da546Spatrick                                               mach_msg_option_t options,
225061da546Spatrick                                               mach_msg_timeout_t timeout,
226061da546Spatrick                                               mach_port_t notify_port) {
227061da546Spatrick   DNBError err;
228061da546Spatrick   const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS);
229061da546Spatrick   mach_msg_timeout_t mach_msg_timeout =
230061da546Spatrick       options & MACH_RCV_TIMEOUT ? timeout : 0;
231061da546Spatrick   if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) {
232061da546Spatrick     // Dump this log message if we have no timeout in case it never returns
233061da546Spatrick     DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = "
234061da546Spatrick                    "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option "
235061da546Spatrick                    "= %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, "
236061da546Spatrick                    "timeout = %u, notify = %#x)",
237061da546Spatrick                    exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
238061da546Spatrick                    exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
239061da546Spatrick                    exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options,
240061da546Spatrick                    (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout,
241061da546Spatrick                    notify_port);
242061da546Spatrick   }
243061da546Spatrick 
244061da546Spatrick   err = ::mach_msg(&exc_msg.hdr,
245061da546Spatrick                    options,              // options
246061da546Spatrick                    0,                    // Send size
247061da546Spatrick                    sizeof(exc_msg.data), // Receive size
248061da546Spatrick                    port,             // exception port to watch for exception on
249061da546Spatrick                    mach_msg_timeout, // timeout in msec (obeyed only if
250061da546Spatrick                                      // MACH_RCV_TIMEOUT is ORed into the
251061da546Spatrick                                      // options parameter)
252061da546Spatrick                    notify_port);
253061da546Spatrick 
254061da546Spatrick   // Dump any errors we get
255061da546Spatrick   if (log_exceptions) {
256061da546Spatrick     err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = "
257061da546Spatrick                     "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
258061da546Spatrick                     "option = %#x, send_size = %u, rcv_size = %u, rcv_name = "
259061da546Spatrick                     "%#x, timeout = %u, notify = %#x)",
260061da546Spatrick                     exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
261061da546Spatrick                     exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
262061da546Spatrick                     exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0,
263061da546Spatrick                     sizeof(exc_msg.data), port, mach_msg_timeout, notify_port);
264061da546Spatrick   }
265061da546Spatrick   return err.Status();
266061da546Spatrick }
267061da546Spatrick 
CatchExceptionRaise(task_t task)268061da546Spatrick bool MachException::Message::CatchExceptionRaise(task_t task) {
269061da546Spatrick   bool success = false;
270061da546Spatrick   state.task_port = task;
271061da546Spatrick   g_message = &state;
272061da546Spatrick   // The exc_server function is the MIG generated server handling function
273061da546Spatrick   // to handle messages from the kernel relating to the occurrence of an
274061da546Spatrick   // exception in a thread. Such messages are delivered to the exception port
275061da546Spatrick   // set via thread_set_exception_ports or task_set_exception_ports. When an
276061da546Spatrick   // exception occurs in a thread, the thread sends an exception message to
277061da546Spatrick   // its exception port, blocking in the kernel waiting for the receipt of a
278061da546Spatrick   // reply. The exc_server function performs all necessary argument handling
279061da546Spatrick   // for this kernel message and calls catch_exception_raise,
280061da546Spatrick   // catch_exception_raise_state or catch_exception_raise_state_identity,
281061da546Spatrick   // which should handle the exception. If the called routine returns
282061da546Spatrick   // KERN_SUCCESS, a reply message will be sent, allowing the thread to
283061da546Spatrick   // continue from the point of the exception; otherwise, no reply message
284061da546Spatrick   // is sent and the called routine must have dealt with the exception
285061da546Spatrick   // thread directly.
286061da546Spatrick   if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) {
287061da546Spatrick     success = true;
288061da546Spatrick   } else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) {
289061da546Spatrick     DNBLogThreaded("mach_exc_server returned zero...");
290061da546Spatrick   }
291061da546Spatrick   g_message = NULL;
292061da546Spatrick   return success;
293061da546Spatrick }
294061da546Spatrick 
Reply(MachProcess * process,int signal)295061da546Spatrick kern_return_t MachException::Message::Reply(MachProcess *process, int signal) {
296061da546Spatrick   // Reply to the exception...
297061da546Spatrick   DNBError err;
298061da546Spatrick 
299061da546Spatrick   // If we had a soft signal, we need to update the thread first so it can
300061da546Spatrick   // continue without signaling
301061da546Spatrick   int soft_signal = state.SoftSignal();
302061da546Spatrick   if (soft_signal) {
303061da546Spatrick     int state_pid = -1;
304061da546Spatrick     if (process->Task().TaskPort() == state.task_port) {
305061da546Spatrick       // This is our task, so we can update the signal to send to it
306061da546Spatrick       state_pid = process->ProcessID();
307061da546Spatrick       soft_signal = signal;
308061da546Spatrick     } else {
309061da546Spatrick       err = ::pid_for_task(state.task_port, &state_pid);
310061da546Spatrick     }
311061da546Spatrick 
312061da546Spatrick     assert(state_pid != -1);
313061da546Spatrick     if (state_pid != -1) {
314061da546Spatrick       errno = 0;
315061da546Spatrick       if (::ptrace(PT_THUPDATE, state_pid,
316061da546Spatrick                    (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0)
317061da546Spatrick         err.SetError(errno, DNBError::POSIX);
318061da546Spatrick       else
319061da546Spatrick         err.Clear();
320061da546Spatrick 
321061da546Spatrick       if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
322061da546Spatrick         err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = "
323061da546Spatrick                         "0x%4.4x, signal = %i)",
324061da546Spatrick                         state_pid, state.thread_port, soft_signal);
325061da546Spatrick     }
326061da546Spatrick   }
327061da546Spatrick 
328061da546Spatrick   DNBLogThreadedIf(
329061da546Spatrick       LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = "
330061da546Spatrick                       "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
331061da546Spatrick                       "option = %#x, send_size = %u, rcv_size = %u, rcv_name = "
332061da546Spatrick                       "%#x, timeout = %u, notify = %#x)",
333061da546Spatrick       reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
334061da546Spatrick       reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
335061da546Spatrick       reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id,
336061da546Spatrick       MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0,
337061da546Spatrick       MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
338061da546Spatrick 
339061da546Spatrick   err = ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
340061da546Spatrick                    reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL,
341061da546Spatrick                    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
342061da546Spatrick 
343061da546Spatrick   if (err.Fail()) {
344061da546Spatrick     if (err.Status() == MACH_SEND_INTERRUPTED) {
345061da546Spatrick       if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
346061da546Spatrick         err.LogThreaded("::mach_msg() - send interrupted");
347061da546Spatrick       // TODO: keep retrying to reply???
348061da546Spatrick     } else {
349061da546Spatrick       if (state.task_port == process->Task().TaskPort()) {
350061da546Spatrick         DNBLogThreaded("error: mach_msg() returned an error when replying to a "
351061da546Spatrick                        "mach exception: error = %u",
352061da546Spatrick                        err.Status());
353061da546Spatrick       } else {
354061da546Spatrick         if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
355061da546Spatrick           err.LogThreaded("::mach_msg() - failed (child of task)");
356061da546Spatrick       }
357061da546Spatrick     }
358061da546Spatrick   }
359061da546Spatrick 
360061da546Spatrick   return err.Status();
361061da546Spatrick }
362061da546Spatrick 
Dump() const363061da546Spatrick void MachException::Data::Dump() const {
364061da546Spatrick   const char *exc_type_name = MachException::Name(exc_type);
365061da546Spatrick   DNBLogThreadedIf(
366061da546Spatrick       LOG_EXCEPTIONS, "    state { task_port = 0x%4.4x, thread_port =  "
367061da546Spatrick                       "0x%4.4x, exc_type = %i (%s) ...",
368061da546Spatrick       task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
369061da546Spatrick 
370061da546Spatrick   const size_t exc_data_count = exc_data.size();
371061da546Spatrick   // Dump any special exception data contents
372061da546Spatrick   int soft_signal = SoftSignal();
373061da546Spatrick   if (soft_signal != 0) {
374061da546Spatrick     const char *sig_str = SysSignal::Name(soft_signal);
375061da546Spatrick     DNBLogThreadedIf(LOG_EXCEPTIONS,
376061da546Spatrick                      "            exc_data: EXC_SOFT_SIGNAL (%i (%s))",
377061da546Spatrick                      soft_signal, sig_str ? sig_str : "unknown signal");
378061da546Spatrick   } else {
379061da546Spatrick     // No special disassembly for this data, just dump the data
380061da546Spatrick     size_t idx;
381061da546Spatrick     for (idx = 0; idx < exc_data_count; ++idx) {
382061da546Spatrick       DNBLogThreadedIf(LOG_EXCEPTIONS, "            exc_data[%llu]: 0x%llx",
383061da546Spatrick                        (uint64_t)idx, (uint64_t)exc_data[idx]);
384061da546Spatrick     }
385061da546Spatrick   }
386061da546Spatrick }
387061da546Spatrick 
388061da546Spatrick // The EXC_MASK_ALL value hard-coded here so that lldb can be built
389061da546Spatrick // on a new OS with an older deployment target .  The new OS may have
390061da546Spatrick // an addition to its EXC_MASK_ALL that the old OS will not recognize -
391061da546Spatrick // <mach/exception_types.h> doesn't vary the value based on the deployment
392061da546Spatrick // target.  So we need a known set of masks that can be assumed to be
393061da546Spatrick // valid when running on an older OS.  We'll fall back to trying
394061da546Spatrick // PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is
395061da546Spatrick // not recognized.
396061da546Spatrick 
397061da546Spatrick #define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS |                \
398061da546Spatrick                          EXC_MASK_BAD_INSTRUCTION |             \
399061da546Spatrick                          EXC_MASK_ARITHMETIC |                  \
400061da546Spatrick                          EXC_MASK_EMULATION |                   \
401061da546Spatrick                          EXC_MASK_SOFTWARE |                    \
402061da546Spatrick                          EXC_MASK_BREAKPOINT |                  \
403061da546Spatrick                          EXC_MASK_SYSCALL |                     \
404061da546Spatrick                          EXC_MASK_MACH_SYSCALL |                \
405061da546Spatrick                          EXC_MASK_RPC_ALERT |                   \
406061da546Spatrick                          EXC_MASK_RESOURCE |                    \
407061da546Spatrick                          EXC_MASK_GUARD |                       \
408061da546Spatrick                          EXC_MASK_MACHINE)
409061da546Spatrick 
410061da546Spatrick #define LLDB_EXC_MASK EXC_MASK_ALL
411061da546Spatrick 
Save(task_t task)412061da546Spatrick kern_return_t MachException::PortInfo::Save(task_t task) {
413061da546Spatrick   DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE,
414061da546Spatrick                    "MachException::PortInfo::Save ( task = 0x%4.4x )", task);
415061da546Spatrick   // Be careful to be able to have debugserver built on a newer OS than what
416061da546Spatrick   // it is currently running on by being able to start with all exceptions
417061da546Spatrick   // and back off to just what is supported on the current system
418061da546Spatrick   DNBError err;
419061da546Spatrick 
420061da546Spatrick   mask = LLDB_EXC_MASK;
421061da546Spatrick 
422061da546Spatrick   count = (sizeof(ports) / sizeof(ports[0]));
423061da546Spatrick   err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors,
424061da546Spatrick                                    flavors);
425061da546Spatrick   if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
426061da546Spatrick     err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, "
427061da546Spatrick                     "maskCnt => %u, ports, behaviors, flavors )",
428061da546Spatrick                     task, mask, count);
429061da546Spatrick 
430061da546Spatrick   if (err.Status() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) {
431061da546Spatrick     mask = PREV_EXC_MASK_ALL;
432061da546Spatrick     count = (sizeof(ports) / sizeof(ports[0]));
433061da546Spatrick     err = ::task_get_exception_ports(task, mask, masks, &count, ports,
434061da546Spatrick                                      behaviors, flavors);
435061da546Spatrick     if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
436061da546Spatrick       err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = "
437061da546Spatrick                       "0x%x, maskCnt => %u, ports, behaviors, flavors )",
438061da546Spatrick                       task, mask, count);
439061da546Spatrick   }
440061da546Spatrick   if (err.Fail()) {
441061da546Spatrick     mask = 0;
442061da546Spatrick     count = 0;
443061da546Spatrick   }
444061da546Spatrick   return err.Status();
445061da546Spatrick }
446061da546Spatrick 
Restore(task_t task)447061da546Spatrick kern_return_t MachException::PortInfo::Restore(task_t task) {
448061da546Spatrick   DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE,
449061da546Spatrick                    "MachException::PortInfo::Restore( task = 0x%4.4x )", task);
450061da546Spatrick   uint32_t i = 0;
451061da546Spatrick   DNBError err;
452061da546Spatrick   if (count > 0) {
453061da546Spatrick     for (i = 0; i < count; i++) {
454061da546Spatrick       err = ::task_set_exception_ports(task, masks[i], ports[i], behaviors[i],
455061da546Spatrick                                        flavors[i]);
456061da546Spatrick       if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) {
457061da546Spatrick         err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, "
458061da546Spatrick                         "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
459061da546Spatrick                         "behavior = 0x%8.8x, new_flavor = 0x%8.8x )",
460061da546Spatrick                         task, masks[i], ports[i], behaviors[i], flavors[i]);
461061da546Spatrick         // Bail if we encounter any errors
462061da546Spatrick       }
463061da546Spatrick 
464061da546Spatrick       if (err.Fail())
465061da546Spatrick         break;
466061da546Spatrick     }
467061da546Spatrick   }
468061da546Spatrick   count = 0;
469061da546Spatrick   return err.Status();
470061da546Spatrick }
471061da546Spatrick 
Name(exception_type_t exc_type)472061da546Spatrick const char *MachException::Name(exception_type_t exc_type) {
473061da546Spatrick   switch (exc_type) {
474061da546Spatrick   case EXC_BAD_ACCESS:
475061da546Spatrick     return "EXC_BAD_ACCESS";
476061da546Spatrick   case EXC_BAD_INSTRUCTION:
477061da546Spatrick     return "EXC_BAD_INSTRUCTION";
478061da546Spatrick   case EXC_ARITHMETIC:
479061da546Spatrick     return "EXC_ARITHMETIC";
480061da546Spatrick   case EXC_EMULATION:
481061da546Spatrick     return "EXC_EMULATION";
482061da546Spatrick   case EXC_SOFTWARE:
483061da546Spatrick     return "EXC_SOFTWARE";
484061da546Spatrick   case EXC_BREAKPOINT:
485061da546Spatrick     return "EXC_BREAKPOINT";
486061da546Spatrick   case EXC_SYSCALL:
487061da546Spatrick     return "EXC_SYSCALL";
488061da546Spatrick   case EXC_MACH_SYSCALL:
489061da546Spatrick     return "EXC_MACH_SYSCALL";
490061da546Spatrick   case EXC_RPC_ALERT:
491061da546Spatrick     return "EXC_RPC_ALERT";
492061da546Spatrick #ifdef EXC_CRASH
493061da546Spatrick   case EXC_CRASH:
494061da546Spatrick     return "EXC_CRASH";
495061da546Spatrick #endif
496061da546Spatrick   case EXC_RESOURCE:
497061da546Spatrick     return "EXC_RESOURCE";
498061da546Spatrick #ifdef EXC_GUARD
499061da546Spatrick   case EXC_GUARD:
500061da546Spatrick     return "EXC_GUARD";
501061da546Spatrick #endif
502061da546Spatrick #ifdef EXC_CORPSE_NOTIFY
503061da546Spatrick   case EXC_CORPSE_NOTIFY:
504061da546Spatrick     return "EXC_CORPSE_NOTIFY";
505061da546Spatrick #endif
506061da546Spatrick #ifdef EXC_CORPSE_VARIANT_BIT
507061da546Spatrick   case EXC_CORPSE_VARIANT_BIT:
508061da546Spatrick     return "EXC_CORPSE_VARIANT_BIT";
509061da546Spatrick #endif
510061da546Spatrick   default:
511061da546Spatrick     break;
512061da546Spatrick   }
513061da546Spatrick   return NULL;
514061da546Spatrick }
515*f6aab3d8Srobert 
516*f6aab3d8Srobert // Returns the exception mask for a given exception name.
517*f6aab3d8Srobert // 0 is not a legit mask, so we return that in the case of an error.
ExceptionMask(const char * name)518*f6aab3d8Srobert exception_mask_t MachException::ExceptionMask(const char *name) {
519*f6aab3d8Srobert   static const char *exception_prefix = "EXC_";
520*f6aab3d8Srobert   static const int prefix_len = strlen(exception_prefix);
521*f6aab3d8Srobert 
522*f6aab3d8Srobert   // All mach exceptions start with this prefix:
523*f6aab3d8Srobert   if (strstr(name, exception_prefix) != name)
524*f6aab3d8Srobert     return 0;
525*f6aab3d8Srobert 
526*f6aab3d8Srobert   name += prefix_len;
527*f6aab3d8Srobert   std::string name_str = name;
528*f6aab3d8Srobert   if (name_str == "BAD_ACCESS")
529*f6aab3d8Srobert     return EXC_MASK_BAD_ACCESS;
530*f6aab3d8Srobert   if (name_str == "BAD_INSTRUCTION")
531*f6aab3d8Srobert     return EXC_MASK_BAD_INSTRUCTION;
532*f6aab3d8Srobert   if (name_str == "ARITHMETIC")
533*f6aab3d8Srobert     return EXC_MASK_ARITHMETIC;
534*f6aab3d8Srobert   if (name_str == "EMULATION")
535*f6aab3d8Srobert     return EXC_MASK_EMULATION;
536*f6aab3d8Srobert   if (name_str == "SOFTWARE")
537*f6aab3d8Srobert     return EXC_MASK_SOFTWARE;
538*f6aab3d8Srobert   if (name_str == "BREAKPOINT")
539*f6aab3d8Srobert     return EXC_MASK_BREAKPOINT;
540*f6aab3d8Srobert   if (name_str == "SYSCALL")
541*f6aab3d8Srobert     return EXC_MASK_SYSCALL;
542*f6aab3d8Srobert   if (name_str == "MACH_SYSCALL")
543*f6aab3d8Srobert     return EXC_MASK_MACH_SYSCALL;
544*f6aab3d8Srobert   if (name_str == "RPC_ALERT")
545*f6aab3d8Srobert     return EXC_MASK_RPC_ALERT;
546*f6aab3d8Srobert #ifdef EXC_CRASH
547*f6aab3d8Srobert   if (name_str == "CRASH")
548*f6aab3d8Srobert     return EXC_MASK_CRASH;
549*f6aab3d8Srobert #endif
550*f6aab3d8Srobert   if (name_str == "RESOURCE")
551*f6aab3d8Srobert     return EXC_MASK_RESOURCE;
552*f6aab3d8Srobert #ifdef EXC_GUARD
553*f6aab3d8Srobert   if (name_str == "GUARD")
554*f6aab3d8Srobert     return EXC_MASK_GUARD;
555*f6aab3d8Srobert #endif
556*f6aab3d8Srobert #ifdef EXC_CORPSE_NOTIFY
557*f6aab3d8Srobert   if (name_str == "CORPSE_NOTIFY")
558*f6aab3d8Srobert     return EXC_MASK_CORPSE_NOTIFY;
559*f6aab3d8Srobert #endif
560*f6aab3d8Srobert   return 0;
561*f6aab3d8Srobert }
562