| 1 | //===-- MachException.cpp ---------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Created by Greg Clayton on 6/18/07. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "MachException.h" |
| 14 | #include "DNB.h" |
| 15 | #include "DNBError.h" |
| 16 | #include "DNBLog.h" |
| 17 | #include "MachProcess.h" |
| 18 | #include "SysSignal.h" |
| 19 | #include <cerrno> |
| 20 | #include <inttypes.h> |
| 21 | #include <sys/ptrace.h> |
| 22 | #include <sys/types.h> |
| 23 | |
| 24 | static void AppendExceptionData(std::vector<mach_exception_data_type_t> &out, |
| 25 | mach_exception_data_t data, |
| 26 | mach_msg_type_number_t count) { |
| 27 | mach_exception_data_type_t buf; |
| 28 | for (mach_msg_type_number_t i = 0; i < count; ++i) { |
| 29 | // The input Data we receive need not be aligned correctly. |
| 30 | // Perform an unaligned copy by pretending we're dealing with |
| 31 | // a char* buffer. This is required to work around UBSAN/ASAN |
| 32 | // "misaligned address" errors. |
| 33 | auto *src = reinterpret_cast<char *>(data + i); |
| 34 | memcpy(&buf, src, sizeof(mach_exception_data_type_t)); |
| 35 | out.push_back(buf); |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | // Routine mach_exception_raise |
| 40 | extern "C" kern_return_t |
| 41 | catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, |
| 42 | mach_port_t task, exception_type_t exception, |
| 43 | mach_exception_data_t code, |
| 44 | mach_msg_type_number_t codeCnt); |
| 45 | |
| 46 | extern "C" kern_return_t catch_mach_exception_raise_state( |
| 47 | mach_port_t exception_port, exception_type_t exception, |
| 48 | const mach_exception_data_t code, mach_msg_type_number_t codeCnt, |
| 49 | int *flavor, const thread_state_t old_state, |
| 50 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
| 51 | mach_msg_type_number_t *new_stateCnt); |
| 52 | |
| 53 | // Routine mach_exception_raise_state_identity |
| 54 | extern "C" kern_return_t catch_mach_exception_raise_state_identity( |
| 55 | mach_port_t exception_port, mach_port_t thread, mach_port_t task, |
| 56 | exception_type_t exception, mach_exception_data_t code, |
| 57 | mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, |
| 58 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
| 59 | mach_msg_type_number_t *new_stateCnt); |
| 60 | |
| 61 | extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP, |
| 62 | mach_msg_header_t *OutHeadP); |
| 63 | |
| 64 | // Note: g_message points to the storage allocated to catch the data from |
| 65 | // catching the current exception raise. It's populated when we catch a raised |
| 66 | // exception which can't immediately be replied to. |
| 67 | // |
| 68 | // If it becomes possible to catch exceptions from multiple threads |
| 69 | // simultaneously, accesses to g_message would need to be mutually exclusive. |
| 70 | static MachException::Data *g_message = NULL; |
| 71 | |
| 72 | extern "C" kern_return_t catch_mach_exception_raise_state( |
| 73 | mach_port_t exc_port, exception_type_t exc_type, |
| 74 | const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, |
| 75 | int *flavor, const thread_state_t old_state, |
| 76 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
| 77 | mach_msg_type_number_t *new_stateCnt) { |
| 78 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
| 79 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data " |
| 80 | "= 0x%llx, exc_data_count = %d)" , |
| 81 | __FUNCTION__, exc_port, exc_type, |
| 82 | MachException::Name(exc_type), (uint64_t)exc_data, |
| 83 | exc_data_count); |
| 84 | } |
| 85 | return KERN_FAILURE; |
| 86 | } |
| 87 | |
| 88 | extern "C" kern_return_t catch_mach_exception_raise_state_identity( |
| 89 | mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, |
| 90 | exception_type_t exc_type, mach_exception_data_t exc_data, |
| 91 | mach_msg_type_number_t exc_data_count, int *flavor, |
| 92 | thread_state_t old_state, mach_msg_type_number_t old_stateCnt, |
| 93 | thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) { |
| 94 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
| 95 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = " |
| 96 | "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, " |
| 97 | "0x%llx })" , |
| 98 | __FUNCTION__, exc_port, thread_port, task_port, exc_type, |
| 99 | MachException::Name(exc_type), exc_data_count, |
| 100 | (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), |
| 101 | (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); |
| 102 | } |
| 103 | |
| 104 | return KERN_FAILURE; |
| 105 | } |
| 106 | |
| 107 | extern "C" kern_return_t |
| 108 | catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, |
| 109 | mach_port_t task_port, exception_type_t exc_type, |
| 110 | mach_exception_data_t exc_data, |
| 111 | mach_msg_type_number_t exc_data_count) { |
| 112 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
| 113 | std::vector<mach_exception_data_type_t> exc_datas; |
| 114 | AppendExceptionData(exc_datas, exc_data, exc_data_count); |
| 115 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = " |
| 116 | "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%" PRIx64 |
| 117 | ", " |
| 118 | "0x%" PRIx64 " })" , |
| 119 | __FUNCTION__, exc_port, thread_port, task_port, exc_type, |
| 120 | MachException::Name(exc_type), exc_data_count, |
| 121 | (exc_data_count > 0 ? exc_datas[0] : 0xBADDBADD), |
| 122 | (exc_data_count > 1 ? exc_datas[1] : 0xBADDBADD)); |
| 123 | } |
| 124 | g_message->exc_type = 0; |
| 125 | g_message->exc_data.clear(); |
| 126 | |
| 127 | if (task_port == g_message->task_port) { |
| 128 | g_message->task_port = task_port; |
| 129 | g_message->thread_port = thread_port; |
| 130 | g_message->exc_type = exc_type; |
| 131 | AppendExceptionData(g_message->exc_data, exc_data, exc_data_count); |
| 132 | return KERN_SUCCESS; |
| 133 | } else if (!MachTask::IsValid(g_message->task_port)) { |
| 134 | // Our original exception port isn't valid anymore check for a SIGTRAP |
| 135 | if (exc_type == EXC_SOFTWARE && exc_data_count == 2 && |
| 136 | exc_data[0] == EXC_SOFT_SIGNAL && exc_data[1] == SIGTRAP) { |
| 137 | // We got a SIGTRAP which indicates we might have exec'ed and possibly |
| 138 | // lost our old task port during the exec, so we just need to switch over |
| 139 | // to using this new task port |
| 140 | g_message->task_port = task_port; |
| 141 | g_message->thread_port = thread_port; |
| 142 | g_message->exc_type = exc_type; |
| 143 | AppendExceptionData(g_message->exc_data, exc_data, exc_data_count); |
| 144 | return KERN_SUCCESS; |
| 145 | } |
| 146 | } |
| 147 | return KERN_FAILURE; |
| 148 | } |
| 149 | |
| 150 | void MachException::Message::Dump() const { |
| 151 | DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_msg { bits = 0x%8.8x size = 0x%8.8x " |
| 152 | "remote-port = 0x%8.8x local-port = 0x%8.8x " |
| 153 | "reserved = 0x%8.8x id = 0x%8.8x } " , |
| 154 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
| 155 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
| 156 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id); |
| 157 | |
| 158 | DNBLogThreadedIf(LOG_EXCEPTIONS, "reply_msg { bits = 0x%8.8x size = 0x%8.8x " |
| 159 | "remote-port = 0x%8.8x local-port = 0x%8.8x " |
| 160 | "reserved = 0x%8.8x id = 0x%8.8x }" , |
| 161 | reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, |
| 162 | reply_msg.hdr.msgh_remote_port, |
| 163 | reply_msg.hdr.msgh_local_port, reply_msg.hdr.msgh_reserved, |
| 164 | reply_msg.hdr.msgh_id); |
| 165 | |
| 166 | state.Dump(); |
| 167 | } |
| 168 | |
| 169 | bool MachException::Data::GetStopInfo( |
| 170 | struct DNBThreadStopInfo *stop_info) const { |
| 171 | // Zero out the structure. |
| 172 | memset(stop_info, 0, sizeof(struct DNBThreadStopInfo)); |
| 173 | |
| 174 | if (exc_type == 0) { |
| 175 | stop_info->reason = eStopTypeInvalid; |
| 176 | return true; |
| 177 | } |
| 178 | |
| 179 | #if defined(__arm64__) || defined(__aarch64__) |
| 180 | if (exc_type == EXC_BREAKPOINT && exc_data[0] == EXC_ARM_DA_DEBUG && |
| 181 | exc_data.size() > 1) { |
| 182 | stop_info->reason = eStopTypeWatchpoint; |
| 183 | stop_info->details.watchpoint.mach_exception_addr = exc_data[1]; |
| 184 | stop_info->details.watchpoint.addr = INVALID_NUB_ADDRESS; |
| 185 | if (exc_data.size() > 2) { |
| 186 | stop_info->details.watchpoint.hw_idx = exc_data[2]; |
| 187 | } |
| 188 | return true; |
| 189 | } |
| 190 | #endif |
| 191 | |
| 192 | // We always stop with a mach exceptions |
| 193 | stop_info->reason = eStopTypeException; |
| 194 | // Save the EXC_XXXX exception type |
| 195 | stop_info->details.exception.type = exc_type; |
| 196 | |
| 197 | // Fill in a text description |
| 198 | const char *exc_name = MachException::Name(exc_type); |
| 199 | char *desc = stop_info->description; |
| 200 | const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH; |
| 201 | if (exc_name) |
| 202 | desc += |
| 203 | snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s" , exc_name); |
| 204 | else |
| 205 | desc += |
| 206 | snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i" , exc_type); |
| 207 | |
| 208 | stop_info->details.exception.data_count = exc_data.size(); |
| 209 | |
| 210 | int soft_signal = SoftSignal(); |
| 211 | if (soft_signal) { |
| 212 | if (desc < end_desc) { |
| 213 | const char *sig_str = SysSignal::Name(soft_signal); |
| 214 | snprintf(s: desc, maxlen: end_desc - desc, format: " EXC_SOFT_SIGNAL( %i ( %s ))" , |
| 215 | soft_signal, sig_str ? sig_str : "unknown signal" ); |
| 216 | } |
| 217 | } else { |
| 218 | // No special disassembly for exception data, just |
| 219 | size_t idx; |
| 220 | if (desc < end_desc) { |
| 221 | desc += snprintf(s: desc, maxlen: end_desc - desc, format: " data[%llu] = {" , |
| 222 | (uint64_t)stop_info->details.exception.data_count); |
| 223 | |
| 224 | for (idx = 0; |
| 225 | desc < end_desc && idx < stop_info->details.exception.data_count; |
| 226 | ++idx) |
| 227 | desc += snprintf( |
| 228 | desc, end_desc - desc, "0x%llx%c" , (uint64_t)exc_data[idx], |
| 229 | ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | // Copy the exception data |
| 234 | size_t i; |
| 235 | for (i = 0; i < stop_info->details.exception.data_count; i++) |
| 236 | stop_info->details.exception.data[i] = exc_data[i]; |
| 237 | |
| 238 | return true; |
| 239 | } |
| 240 | |
| 241 | void MachException::Data::DumpStopReason() const { |
| 242 | int soft_signal = SoftSignal(); |
| 243 | if (soft_signal) { |
| 244 | const char *signal_str = SysSignal::Name(soft_signal); |
| 245 | if (signal_str) |
| 246 | DNBLog("signal(%s)" , signal_str); |
| 247 | else |
| 248 | DNBLog("signal(%i)" , soft_signal); |
| 249 | return; |
| 250 | } |
| 251 | DNBLog("%s" , Name(exc_type)); |
| 252 | } |
| 253 | |
| 254 | kern_return_t MachException::Message::Receive(mach_port_t port, |
| 255 | mach_msg_option_t options, |
| 256 | mach_msg_timeout_t timeout, |
| 257 | mach_port_t notify_port) { |
| 258 | DNBError err; |
| 259 | const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS); |
| 260 | mach_msg_timeout_t mach_msg_timeout = |
| 261 | (options & MACH_RCV_TIMEOUT) ? timeout : 0; |
| 262 | if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) { |
| 263 | // Dump this log message if we have no timeout in case it never returns |
| 264 | DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = " |
| 265 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option " |
| 266 | "= %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, " |
| 267 | "timeout = %u, notify = %#x)" , |
| 268 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
| 269 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
| 270 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, |
| 271 | (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout, |
| 272 | notify_port); |
| 273 | } |
| 274 | |
| 275 | err = ::mach_msg(&exc_msg.hdr, |
| 276 | options, // options |
| 277 | 0, // Send size |
| 278 | sizeof(exc_msg.data), // Receive size |
| 279 | port, // exception port to watch for exception on |
| 280 | mach_msg_timeout, // timeout in msec (obeyed only if |
| 281 | // MACH_RCV_TIMEOUT is ORed into the |
| 282 | // options parameter) |
| 283 | notify_port); |
| 284 | |
| 285 | // Dump any errors we get |
| 286 | if (log_exceptions) { |
| 287 | err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = " |
| 288 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " |
| 289 | "option = %#x, send_size = %u, rcv_size = %u, rcv_name = " |
| 290 | "%#x, timeout = %u, notify = %#x)" , |
| 291 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
| 292 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
| 293 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, |
| 294 | sizeof(exc_msg.data), port, mach_msg_timeout, notify_port); |
| 295 | } |
| 296 | return err.Status(); |
| 297 | } |
| 298 | |
| 299 | bool MachException::Message::CatchExceptionRaise(task_t task) { |
| 300 | bool success = false; |
| 301 | state.task_port = task; |
| 302 | g_message = &state; |
| 303 | // The exc_server function is the MIG generated server handling function |
| 304 | // to handle messages from the kernel relating to the occurrence of an |
| 305 | // exception in a thread. Such messages are delivered to the exception port |
| 306 | // set via thread_set_exception_ports or task_set_exception_ports. When an |
| 307 | // exception occurs in a thread, the thread sends an exception message to |
| 308 | // its exception port, blocking in the kernel waiting for the receipt of a |
| 309 | // reply. The exc_server function performs all necessary argument handling |
| 310 | // for this kernel message and calls catch_exception_raise, |
| 311 | // catch_exception_raise_state or catch_exception_raise_state_identity, |
| 312 | // which should handle the exception. If the called routine returns |
| 313 | // KERN_SUCCESS, a reply message will be sent, allowing the thread to |
| 314 | // continue from the point of the exception; otherwise, no reply message |
| 315 | // is sent and the called routine must have dealt with the exception |
| 316 | // thread directly. |
| 317 | if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) { |
| 318 | success = true; |
| 319 | } else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
| 320 | DNBLogThreaded("mach_exc_server returned zero..." ); |
| 321 | } |
| 322 | g_message = NULL; |
| 323 | return success; |
| 324 | } |
| 325 | |
| 326 | kern_return_t MachException::Message::Reply(MachProcess *process, int signal) { |
| 327 | // Reply to the exception... |
| 328 | DNBError err; |
| 329 | |
| 330 | // If we had a soft signal, we need to update the thread first so it can |
| 331 | // continue without signaling |
| 332 | int soft_signal = state.SoftSignal(); |
| 333 | if (soft_signal) { |
| 334 | int state_pid = -1; |
| 335 | if (process->Task().TaskPort() == state.task_port) { |
| 336 | // This is our task, so we can update the signal to send to it |
| 337 | state_pid = process->ProcessID(); |
| 338 | soft_signal = signal; |
| 339 | } else { |
| 340 | err = ::pid_for_task(state.task_port, &state_pid); |
| 341 | } |
| 342 | |
| 343 | assert(state_pid != -1); |
| 344 | if (state_pid != -1) { |
| 345 | errno = 0; |
| 346 | if (::ptrace(PT_THUPDATE, state_pid, |
| 347 | (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0) |
| 348 | err.SetError(errno, DNBError::POSIX); |
| 349 | else |
| 350 | err.Clear(); |
| 351 | |
| 352 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
| 353 | err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = " |
| 354 | "0x%4.4x, signal = %i)" , |
| 355 | state_pid, state.thread_port, soft_signal); |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | DNBLogThreadedIf( |
| 360 | LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = " |
| 361 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " |
| 362 | "option = %#x, send_size = %u, rcv_size = %u, rcv_name = " |
| 363 | "%#x, timeout = %u, notify = %#x)" , |
| 364 | reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, |
| 365 | reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, |
| 366 | reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id, |
| 367 | MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, |
| 368 | MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| 369 | |
| 370 | err = ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, |
| 371 | reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, |
| 372 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| 373 | |
| 374 | if (err.Fail()) { |
| 375 | if (err.Status() == MACH_SEND_INTERRUPTED) { |
| 376 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) |
| 377 | err.LogThreaded("::mach_msg() - send interrupted" ); |
| 378 | // TODO: keep retrying to reply??? |
| 379 | } else { |
| 380 | if (state.task_port == process->Task().TaskPort()) { |
| 381 | DNBLogThreaded("error: mach_msg() returned an error when replying to a " |
| 382 | "mach exception: error = %u" , |
| 383 | err.Status()); |
| 384 | } else { |
| 385 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) |
| 386 | err.LogThreaded("::mach_msg() - failed (child of task)" ); |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | return err.Status(); |
| 392 | } |
| 393 | |
| 394 | void MachException::Data::Dump() const { |
| 395 | const char *exc_type_name = MachException::Name(exc_type); |
| 396 | DNBLogThreadedIf( |
| 397 | LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = " |
| 398 | "0x%4.4x, exc_type = %i (%s) ..." , |
| 399 | task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???" ); |
| 400 | |
| 401 | const size_t exc_data_count = exc_data.size(); |
| 402 | // Dump any special exception data contents |
| 403 | int soft_signal = SoftSignal(); |
| 404 | if (soft_signal != 0) { |
| 405 | const char *sig_str = SysSignal::Name(soft_signal); |
| 406 | DNBLogThreadedIf(LOG_EXCEPTIONS, |
| 407 | " exc_data: EXC_SOFT_SIGNAL (%i (%s))" , |
| 408 | soft_signal, sig_str ? sig_str : "unknown signal" ); |
| 409 | } else { |
| 410 | // No special disassembly for this data, just dump the data |
| 411 | size_t idx; |
| 412 | for (idx = 0; idx < exc_data_count; ++idx) { |
| 413 | DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%llu]: 0x%llx" , |
| 414 | (uint64_t)idx, (uint64_t)exc_data[idx]); |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | // The EXC_MASK_ALL value hard-coded here so that lldb can be built |
| 420 | // on a new OS with an older deployment target . The new OS may have |
| 421 | // an addition to its EXC_MASK_ALL that the old OS will not recognize - |
| 422 | // <mach/exception_types.h> doesn't vary the value based on the deployment |
| 423 | // target. So we need a known set of masks that can be assumed to be |
| 424 | // valid when running on an older OS. We'll fall back to trying |
| 425 | // PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is |
| 426 | // not recognized. |
| 427 | |
| 428 | #define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \ |
| 429 | EXC_MASK_BAD_INSTRUCTION | \ |
| 430 | EXC_MASK_ARITHMETIC | \ |
| 431 | EXC_MASK_EMULATION | \ |
| 432 | EXC_MASK_SOFTWARE | \ |
| 433 | EXC_MASK_BREAKPOINT | \ |
| 434 | EXC_MASK_SYSCALL | \ |
| 435 | EXC_MASK_MACH_SYSCALL | \ |
| 436 | EXC_MASK_RPC_ALERT | \ |
| 437 | EXC_MASK_RESOURCE | \ |
| 438 | EXC_MASK_GUARD | \ |
| 439 | EXC_MASK_MACHINE) |
| 440 | |
| 441 | #define LLDB_EXC_MASK EXC_MASK_ALL |
| 442 | |
| 443 | kern_return_t MachException::PortInfo::Save(task_t task) { |
| 444 | DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, |
| 445 | "MachException::PortInfo::Save ( task = 0x%4.4x )" , task); |
| 446 | // Be careful to be able to have debugserver built on a newer OS than what |
| 447 | // it is currently running on by being able to start with all exceptions |
| 448 | // and back off to just what is supported on the current system |
| 449 | DNBError err; |
| 450 | |
| 451 | mask = LLDB_EXC_MASK; |
| 452 | |
| 453 | count = (sizeof(ports) / sizeof(ports[0])); |
| 454 | err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors, |
| 455 | flavors); |
| 456 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
| 457 | err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, " |
| 458 | "maskCnt => %u, ports, behaviors, flavors )" , |
| 459 | task, mask, count); |
| 460 | |
| 461 | if (err.Status() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) { |
| 462 | mask = PREV_EXC_MASK_ALL; |
| 463 | count = (sizeof(ports) / sizeof(ports[0])); |
| 464 | err = ::task_get_exception_ports(task, mask, masks, &count, ports, |
| 465 | behaviors, flavors); |
| 466 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
| 467 | err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = " |
| 468 | "0x%x, maskCnt => %u, ports, behaviors, flavors )" , |
| 469 | task, mask, count); |
| 470 | } |
| 471 | if (err.Fail()) { |
| 472 | mask = 0; |
| 473 | count = 0; |
| 474 | } |
| 475 | return err.Status(); |
| 476 | } |
| 477 | |
| 478 | kern_return_t MachException::PortInfo::Restore(task_t task) { |
| 479 | DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, |
| 480 | "MachException::PortInfo::Restore( task = 0x%4.4x )" , task); |
| 481 | uint32_t i = 0; |
| 482 | DNBError err; |
| 483 | if (count > 0) { |
| 484 | for (i = 0; i < count; i++) { |
| 485 | err = ::task_set_exception_ports(task, masks[i], ports[i], behaviors[i], |
| 486 | flavors[i]); |
| 487 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) { |
| 488 | err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, " |
| 489 | "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " |
| 490 | "behavior = 0x%8.8x, new_flavor = 0x%8.8x )" , |
| 491 | task, masks[i], ports[i], behaviors[i], flavors[i]); |
| 492 | // Bail if we encounter any errors |
| 493 | } |
| 494 | |
| 495 | if (err.Fail()) |
| 496 | break; |
| 497 | } |
| 498 | } |
| 499 | count = 0; |
| 500 | return err.Status(); |
| 501 | } |
| 502 | |
| 503 | const char *MachException::Name(exception_type_t exc_type) { |
| 504 | switch (exc_type) { |
| 505 | case EXC_BAD_ACCESS: |
| 506 | return "EXC_BAD_ACCESS" ; |
| 507 | case EXC_BAD_INSTRUCTION: |
| 508 | return "EXC_BAD_INSTRUCTION" ; |
| 509 | case EXC_ARITHMETIC: |
| 510 | return "EXC_ARITHMETIC" ; |
| 511 | case EXC_EMULATION: |
| 512 | return "EXC_EMULATION" ; |
| 513 | case EXC_SOFTWARE: |
| 514 | return "EXC_SOFTWARE" ; |
| 515 | case EXC_BREAKPOINT: |
| 516 | return "EXC_BREAKPOINT" ; |
| 517 | case EXC_SYSCALL: |
| 518 | return "EXC_SYSCALL" ; |
| 519 | case EXC_MACH_SYSCALL: |
| 520 | return "EXC_MACH_SYSCALL" ; |
| 521 | case EXC_RPC_ALERT: |
| 522 | return "EXC_RPC_ALERT" ; |
| 523 | #ifdef EXC_CRASH |
| 524 | case EXC_CRASH: |
| 525 | return "EXC_CRASH" ; |
| 526 | #endif |
| 527 | case EXC_RESOURCE: |
| 528 | return "EXC_RESOURCE" ; |
| 529 | #ifdef EXC_GUARD |
| 530 | case EXC_GUARD: |
| 531 | return "EXC_GUARD" ; |
| 532 | #endif |
| 533 | #ifdef EXC_CORPSE_NOTIFY |
| 534 | case EXC_CORPSE_NOTIFY: |
| 535 | return "EXC_CORPSE_NOTIFY" ; |
| 536 | #endif |
| 537 | #ifdef EXC_CORPSE_VARIANT_BIT |
| 538 | case EXC_CORPSE_VARIANT_BIT: |
| 539 | return "EXC_CORPSE_VARIANT_BIT" ; |
| 540 | #endif |
| 541 | default: |
| 542 | break; |
| 543 | } |
| 544 | return NULL; |
| 545 | } |
| 546 | |
| 547 | // Returns the exception mask for a given exception name. |
| 548 | // 0 is not a legit mask, so we return that in the case of an error. |
| 549 | exception_mask_t MachException::ExceptionMask(const char *name) { |
| 550 | static const char *exception_prefix = "EXC_" ; |
| 551 | static const int prefix_len = strlen(exception_prefix); |
| 552 | |
| 553 | // All mach exceptions start with this prefix: |
| 554 | if (strstr(name, exception_prefix) != name) |
| 555 | return 0; |
| 556 | |
| 557 | name += prefix_len; |
| 558 | std::string name_str = name; |
| 559 | if (name_str == "BAD_ACCESS" ) |
| 560 | return EXC_MASK_BAD_ACCESS; |
| 561 | if (name_str == "BAD_INSTRUCTION" ) |
| 562 | return EXC_MASK_BAD_INSTRUCTION; |
| 563 | if (name_str == "ARITHMETIC" ) |
| 564 | return EXC_MASK_ARITHMETIC; |
| 565 | if (name_str == "EMULATION" ) |
| 566 | return EXC_MASK_EMULATION; |
| 567 | if (name_str == "SOFTWARE" ) |
| 568 | return EXC_MASK_SOFTWARE; |
| 569 | if (name_str == "BREAKPOINT" ) |
| 570 | return EXC_MASK_BREAKPOINT; |
| 571 | if (name_str == "SYSCALL" ) |
| 572 | return EXC_MASK_SYSCALL; |
| 573 | if (name_str == "MACH_SYSCALL" ) |
| 574 | return EXC_MASK_MACH_SYSCALL; |
| 575 | if (name_str == "RPC_ALERT" ) |
| 576 | return EXC_MASK_RPC_ALERT; |
| 577 | #ifdef EXC_CRASH |
| 578 | if (name_str == "CRASH" ) |
| 579 | return EXC_MASK_CRASH; |
| 580 | #endif |
| 581 | if (name_str == "RESOURCE" ) |
| 582 | return EXC_MASK_RESOURCE; |
| 583 | #ifdef EXC_GUARD |
| 584 | if (name_str == "GUARD" ) |
| 585 | return EXC_MASK_GUARD; |
| 586 | #endif |
| 587 | #ifdef EXC_CORPSE_NOTIFY |
| 588 | if (name_str == "CORPSE_NOTIFY" ) |
| 589 | return EXC_MASK_CORPSE_NOTIFY; |
| 590 | #endif |
| 591 | return 0; |
| 592 | } |
| 593 | |