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