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