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