1 //===-- MachException.cpp ---------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Created by Greg Clayton on 6/18/07. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "MachException.h" 15 16 // C includes 17 #include <errno.h> 18 #include <sys/ptrace.h> 19 #include <sys/types.h> 20 21 // C++ includes 22 #include <mutex> 23 24 // LLDB includes 25 #include "lldb/Target/UnixSignals.h" 26 #include "lldb/Utility/LLDBAssert.h" 27 #include "lldb/Utility/Log.h" 28 #include "lldb/Utility/Status.h" 29 #include "lldb/Utility/Stream.h" 30 31 using namespace lldb; 32 using namespace lldb_private; 33 using namespace lldb_private::process_darwin; 34 35 // Routine mach_exception_raise 36 extern "C" kern_return_t 37 catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, 38 mach_port_t task, exception_type_t exception, 39 mach_exception_data_t code, 40 mach_msg_type_number_t codeCnt); 41 42 extern "C" kern_return_t catch_mach_exception_raise_state( 43 mach_port_t exception_port, exception_type_t exception, 44 const mach_exception_data_t code, mach_msg_type_number_t codeCnt, 45 int *flavor, const thread_state_t old_state, 46 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 47 mach_msg_type_number_t *new_stateCnt); 48 49 // Routine mach_exception_raise_state_identity 50 extern "C" kern_return_t catch_mach_exception_raise_state_identity( 51 mach_port_t exception_port, mach_port_t thread, mach_port_t task, 52 exception_type_t exception, mach_exception_data_t code, 53 mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, 54 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 55 mach_msg_type_number_t *new_stateCnt); 56 57 extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP, 58 mach_msg_header_t *OutHeadP); 59 60 static MachException::Data *g_message = NULL; 61 62 extern "C" kern_return_t catch_mach_exception_raise_state( 63 mach_port_t exc_port, exception_type_t exc_type, 64 const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, 65 int *flavor, const thread_state_t old_state, 66 mach_msg_type_number_t old_stateCnt, thread_state_t new_state, 67 mach_msg_type_number_t *new_stateCnt) { 68 // TODO change to LIBLLDB_LOG_EXCEPTION 69 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 70 if (log) { 71 log->Printf("::%s(exc_port = 0x%4.4x, exc_type = %d (%s), " 72 "exc_data = 0x%llx, exc_data_count = %d)", 73 __FUNCTION__, exc_port, exc_type, MachException::Name(exc_type), 74 (uint64_t)exc_data, exc_data_count); 75 } 76 return KERN_FAILURE; 77 } 78 79 extern "C" kern_return_t catch_mach_exception_raise_state_identity( 80 mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, 81 exception_type_t exc_type, mach_exception_data_t exc_data, 82 mach_msg_type_number_t exc_data_count, int *flavor, 83 thread_state_t old_state, mach_msg_type_number_t old_stateCnt, 84 thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) { 85 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 86 if (log) { 87 log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, " 88 "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] = " 89 "{ 0x%llx, 0x%llx })", 90 __FUNCTION__, exc_port, thread_port, task_port, exc_type, 91 MachException::Name(exc_type), exc_data_count, 92 (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), 93 (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); 94 } 95 96 return KERN_FAILURE; 97 } 98 99 extern "C" kern_return_t 100 catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, 101 mach_port_t task_port, exception_type_t exc_type, 102 mach_exception_data_t exc_data, 103 mach_msg_type_number_t exc_data_count) { 104 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 105 if (log) { 106 log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, " 107 "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] " 108 "= { 0x%llx, 0x%llx })", 109 __FUNCTION__, exc_port, thread_port, task_port, exc_type, 110 MachException::Name(exc_type), exc_data_count, 111 (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), 112 (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); 113 } 114 115 if (task_port == g_message->task_port) { 116 g_message->task_port = task_port; 117 g_message->thread_port = thread_port; 118 g_message->exc_type = exc_type; 119 g_message->exc_data.resize(exc_data_count); 120 ::memcpy(&g_message->exc_data[0], exc_data, 121 g_message->exc_data.size() * sizeof(mach_exception_data_type_t)); 122 return KERN_SUCCESS; 123 } 124 return KERN_FAILURE; 125 } 126 127 bool MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info, 128 const UnixSignals &signals, 129 Stream &stream) const { 130 if (!stop_info) 131 return false; 132 133 // Zero out the structure. 134 memset(stop_info, 0, sizeof(struct ThreadStopInfo)); 135 136 if (exc_type == 0) { 137 stop_info->reason = eStopReasonInvalid; 138 return true; 139 } 140 141 // We always stop with a mach exception. 142 stop_info->reason = eStopReasonException; 143 // Save the EXC_XXXX exception type. 144 stop_info->details.exception.type = exc_type; 145 146 // Fill in a text description 147 const char *exc_name = MachException::Name(exc_type); 148 if (exc_name) 149 stream.Printf("%s", exc_name); 150 else 151 stream.Printf("%i", exc_type); 152 153 stop_info->details.exception.data_count = exc_data.size(); 154 155 int soft_signal = SoftSignal(); 156 if (soft_signal) { 157 const char *sig_str = signals.GetSignalAsCString(soft_signal); 158 stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, 159 sig_str ? sig_str : "unknown signal"); 160 } else { 161 // No special disassembly for exception data, just print it. 162 size_t idx; 163 stream.Printf(" data[%llu] = {", 164 (uint64_t)stop_info->details.exception.data_count); 165 166 for (idx = 0; idx < stop_info->details.exception.data_count; ++idx) { 167 stream.Printf( 168 "0x%llx%c", (uint64_t)exc_data[idx], 169 ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); 170 } 171 } 172 173 // Copy the exception data 174 for (size_t i = 0; i < stop_info->details.exception.data_count; i++) 175 stop_info->details.exception.data[i] = exc_data[i]; 176 177 return true; 178 } 179 180 Status MachException::Message::Receive(mach_port_t port, 181 mach_msg_option_t options, 182 mach_msg_timeout_t timeout, 183 mach_port_t notify_port) { 184 Status error; 185 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 186 187 mach_msg_timeout_t mach_msg_timeout = 188 options & MACH_RCV_TIMEOUT ? timeout : 0; 189 if (log && ((options & MACH_RCV_TIMEOUT) == 0)) { 190 // Dump this log message if we have no timeout in case it never returns 191 log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, " 192 "local_port = %#x, reserved = 0x%x, id = 0x%x}, " 193 "option = %#x, send_size = 0, rcv_size = %llu, " 194 "rcv_name = %#x, timeout = %u, notify = %#x)", 195 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 196 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 197 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 198 (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout, 199 notify_port); 200 } 201 202 mach_msg_return_t mach_err = 203 ::mach_msg(&exc_msg.hdr, 204 options, // options 205 0, // Send size 206 sizeof(exc_msg.data), // Receive size 207 port, // exception port to watch for 208 // exception on 209 mach_msg_timeout, // timeout in msec (obeyed only 210 // if MACH_RCV_TIMEOUT is ORed 211 // into the options parameter) 212 notify_port); 213 error.SetError(mach_err, eErrorTypeMachKernel); 214 215 // Dump any errors we get 216 if (error.Fail() && log) { 217 log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, " 218 "local_port = %#x, reserved = 0x%x, id = 0x%x}, " 219 "option = %#x, send_size = %u, rcv_size = %lu, rcv_name " 220 "= %#x, timeout = %u, notify = %#x) failed: %s", 221 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 222 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 223 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, 224 sizeof(exc_msg.data), port, mach_msg_timeout, notify_port, 225 error.AsCString()); 226 } 227 return error; 228 } 229 230 void MachException::Message::Dump(Stream &stream) const { 231 stream.Printf(" exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = " 232 "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = " 233 "0x%8.8x }\n", 234 exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, 235 exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, 236 exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id); 237 238 stream.Printf(" reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = " 239 "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = " 240 "0x%8.8x }", 241 reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, 242 reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, 243 reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id); 244 } 245 246 bool MachException::Message::CatchExceptionRaise(task_t task) { 247 bool success = false; 248 state.task_port = task; 249 g_message = &state; 250 // The exc_server function is the MIG generated server handling function to 251 // handle messages from the kernel relating to the occurrence of an exception 252 // in a thread. Such messages are delivered to the exception port set via 253 // thread_set_exception_ports or task_set_exception_ports. When an exception 254 // occurs in a thread, the thread sends an exception message to its exception 255 // port, blocking in the kernel waiting for the receipt of a reply. The 256 // exc_server function performs all necessary argument handling for this 257 // kernel message and calls catch_exception_raise, 258 // catch_exception_raise_state or catch_exception_raise_state_identity, which 259 // should handle the exception. If the called routine returns KERN_SUCCESS, a 260 // reply message will be sent, allowing the thread to continue from the point 261 // of the exception; otherwise, no reply message is sent and the called 262 // routine must have dealt with the exception thread directly. 263 if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) { 264 success = true; 265 } else { 266 Log *log( 267 GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 268 if (log) 269 log->Printf("MachException::Message::%s(): mach_exc_server " 270 "returned zero...", 271 __FUNCTION__); 272 } 273 g_message = NULL; 274 return success; 275 } 276 277 Status MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task, 278 int signal) { 279 // Reply to the exception... 280 Status error; 281 282 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 283 284 // If we had a soft signal, we need to update the thread first so it can 285 // continue without signaling 286 int soft_signal = state.SoftSignal(); 287 if (soft_signal) { 288 int state_pid = -1; 289 if (inferior_task == state.task_port) { 290 // This is our task, so we can update the signal to send to it 291 state_pid = inferior_pid; 292 soft_signal = signal; 293 } else { 294 auto mach_err = ::pid_for_task(state.task_port, &state_pid); 295 if (mach_err) { 296 error.SetError(mach_err, eErrorTypeMachKernel); 297 if (log) 298 log->Printf("MachException::Message::%s(): pid_for_task() " 299 "failed: %s", 300 __FUNCTION__, error.AsCString()); 301 return error; 302 } 303 } 304 305 lldbassert(state_pid != -1); 306 if (state_pid != -1) { 307 errno = 0; 308 caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port; 309 if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal) != 0) 310 error.SetError(errno, eErrorTypePOSIX); 311 312 if (!error.Success()) { 313 if (log) 314 log->Printf("::ptrace(request = PT_THUPDATE, pid = " 315 "0x%4.4x, tid = 0x%4.4x, signal = %i)", 316 state_pid, state.thread_port, soft_signal); 317 return error; 318 } 319 } 320 } 321 322 if (log) 323 log->Printf("::mach_msg ( msg->{bits = %#x, size = %u, remote_port " 324 "= %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " 325 "option = %#x, send_size = %u, rcv_size = %u, rcv_name " 326 "= %#x, timeout = %u, notify = %#x)", 327 reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, 328 reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, 329 reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id, 330 MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, 331 MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 332 333 auto mach_err = 334 ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, 335 reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, 336 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 337 if (mach_err) 338 error.SetError(mach_err, eErrorTypeMachKernel); 339 340 // Log our error if we have one. 341 if (error.Fail() && log) { 342 if (error.GetError() == MACH_SEND_INTERRUPTED) { 343 log->PutCString("::mach_msg() - send interrupted"); 344 // TODO: keep retrying to reply??? 345 } else if (state.task_port == inferior_task) { 346 log->Printf("mach_msg(): returned an error when replying " 347 "to a mach exception: error = %u (%s)", 348 error.GetError(), error.AsCString()); 349 } else { 350 log->Printf("::mach_msg() - failed (child of task): %u (%s)", 351 error.GetError(), error.AsCString()); 352 } 353 } 354 355 return error; 356 } 357 358 #define PREV_EXC_MASK_ALL \ 359 (EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | \ 360 EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT | \ 361 EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT | \ 362 EXC_MASK_MACHINE) 363 364 // Don't listen for EXC_RESOURCE, it should really get handled by the system 365 // handler. 366 367 #ifndef EXC_RESOURCE 368 #define EXC_RESOURCE 11 369 #endif 370 371 #ifndef EXC_MASK_RESOURCE 372 #define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) 373 #endif 374 375 #define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE) 376 377 Status MachException::PortInfo::Save(task_t task) { 378 Status error; 379 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 380 381 if (log) 382 log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__, 383 task); 384 385 // Be careful to be able to have debugserver built on a newer OS than what it 386 // is currently running on by being able to start with all exceptions and 387 // back off to just what is supported on the current system 388 mask = LLDB_EXC_MASK; 389 390 count = (sizeof(ports) / sizeof(ports[0])); 391 auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, 392 behaviors, flavors); 393 if (mach_err) 394 error.SetError(mach_err, eErrorTypeMachKernel); 395 396 if (log) { 397 if (error.Success()) { 398 log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = " 399 "0x%x, maskCnt => %u, ports, behaviors, flavors)", 400 task, mask, count); 401 } else { 402 log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, " 403 "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)", 404 task, mask, count, error.GetError(), error.AsCString()); 405 } 406 } 407 408 if ((error.GetError() == KERN_INVALID_ARGUMENT) && 409 (mask != PREV_EXC_MASK_ALL)) { 410 mask = PREV_EXC_MASK_ALL; 411 count = (sizeof(ports) / sizeof(ports[0])); 412 mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports, 413 behaviors, flavors); 414 error.SetError(mach_err, eErrorTypeMachKernel); 415 if (log) { 416 if (error.Success()) { 417 log->Printf("::task_get_exception_ports(task = 0x%4.4x, " 418 "mask = 0x%x, maskCnt => %u, ports, behaviors, " 419 "flavors)", 420 task, mask, count); 421 } else { 422 log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = " 423 "0x%x, maskCnt => %u, ports, behaviors, flavors) " 424 "error: %u (%s)", 425 task, mask, count, error.GetError(), error.AsCString()); 426 } 427 } 428 } 429 if (error.Fail()) { 430 mask = 0; 431 count = 0; 432 } 433 return error; 434 } 435 436 Status MachException::PortInfo::Restore(task_t task) { 437 Status error; 438 439 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); 440 441 if (log) 442 log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task); 443 444 uint32_t i = 0; 445 if (count > 0) { 446 for (i = 0; i < count; i++) { 447 auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i], 448 behaviors[i], flavors[i]); 449 if (mach_err) 450 error.SetError(mach_err, eErrorTypeMachKernel); 451 if (log) { 452 if (error.Success()) { 453 log->Printf("::task_set_exception_ports(task = 0x%4.4x, " 454 "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " 455 "behavior = 0x%8.8x, new_flavor = 0x%8.8x)", 456 task, masks[i], ports[i], behaviors[i], flavors[i]); 457 } else { 458 log->Printf("::task_set_exception_ports(task = 0x%4.4x, " 459 "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " 460 "behavior = 0x%8.8x, new_flavor = 0x%8.8x): " 461 "error %u (%s)", 462 task, masks[i], ports[i], behaviors[i], flavors[i], 463 error.GetError(), error.AsCString()); 464 } 465 } 466 467 // Bail if we encounter any errors 468 if (error.Fail()) 469 break; 470 } 471 } 472 473 count = 0; 474 return error; 475 } 476 477 const char *MachException::Name(exception_type_t exc_type) { 478 switch (exc_type) { 479 case EXC_BAD_ACCESS: 480 return "EXC_BAD_ACCESS"; 481 case EXC_BAD_INSTRUCTION: 482 return "EXC_BAD_INSTRUCTION"; 483 case EXC_ARITHMETIC: 484 return "EXC_ARITHMETIC"; 485 case EXC_EMULATION: 486 return "EXC_EMULATION"; 487 case EXC_SOFTWARE: 488 return "EXC_SOFTWARE"; 489 case EXC_BREAKPOINT: 490 return "EXC_BREAKPOINT"; 491 case EXC_SYSCALL: 492 return "EXC_SYSCALL"; 493 case EXC_MACH_SYSCALL: 494 return "EXC_MACH_SYSCALL"; 495 case EXC_RPC_ALERT: 496 return "EXC_RPC_ALERT"; 497 #ifdef EXC_CRASH 498 case EXC_CRASH: 499 return "EXC_CRASH"; 500 #endif 501 default: 502 break; 503 } 504 return NULL; 505 } 506