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