1 //===-- StopInfoMachException.cpp -----------------------------------------===// 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 #include "StopInfoMachException.h" 10 11 #include "lldb/lldb-forward.h" 12 13 #if defined(__APPLE__) 14 // Needed for the EXC_RESOURCE interpretation macros 15 #include <kern/exc_resource.h> 16 #endif 17 18 #include "lldb/Breakpoint/Watchpoint.h" 19 #include "lldb/Symbol/Symbol.h" 20 #include "lldb/Target/DynamicLoader.h" 21 #include "lldb/Target/ExecutionContext.h" 22 #include "lldb/Target/Process.h" 23 #include "lldb/Target/RegisterContext.h" 24 #include "lldb/Target/Target.h" 25 #include "lldb/Target/Thread.h" 26 #include "lldb/Target/ThreadPlan.h" 27 #include "lldb/Target/UnixSignals.h" 28 #include "lldb/Utility/StreamString.h" 29 30 using namespace lldb; 31 using namespace lldb_private; 32 33 const char *StopInfoMachException::GetDescription() { 34 if (!m_description.empty()) 35 return m_description.c_str(); 36 if (GetValue() == eStopReasonInvalid) 37 return "invalid stop reason!"; 38 39 ExecutionContext exe_ctx(m_thread_wp.lock()); 40 Target *target = exe_ctx.GetTargetPtr(); 41 const llvm::Triple::ArchType cpu = 42 target ? target->GetArchitecture().GetMachine() 43 : llvm::Triple::UnknownArch; 44 45 const char *exc_desc = nullptr; 46 const char *code_label = "code"; 47 const char *code_desc = nullptr; 48 const char *subcode_label = "subcode"; 49 const char *subcode_desc = nullptr; 50 51 #if defined(__APPLE__) 52 char code_desc_buf[32]; 53 char subcode_desc_buf[32]; 54 #endif 55 56 switch (m_value) { 57 case 1: // EXC_BAD_ACCESS 58 exc_desc = "EXC_BAD_ACCESS"; 59 subcode_label = "address"; 60 switch (cpu) { 61 case llvm::Triple::x86: 62 case llvm::Triple::x86_64: 63 switch (m_exc_code) { 64 case 0xd: 65 code_desc = "EXC_I386_GPFLT"; 66 m_exc_data_count = 1; 67 break; 68 } 69 break; 70 case llvm::Triple::arm: 71 case llvm::Triple::thumb: 72 switch (m_exc_code) { 73 case 0x101: 74 code_desc = "EXC_ARM_DA_ALIGN"; 75 break; 76 case 0x102: 77 code_desc = "EXC_ARM_DA_DEBUG"; 78 break; 79 } 80 break; 81 82 default: 83 break; 84 } 85 break; 86 87 case 2: // EXC_BAD_INSTRUCTION 88 exc_desc = "EXC_BAD_INSTRUCTION"; 89 switch (cpu) { 90 case llvm::Triple::x86: 91 case llvm::Triple::x86_64: 92 if (m_exc_code == 1) 93 code_desc = "EXC_I386_INVOP"; 94 break; 95 96 case llvm::Triple::arm: 97 case llvm::Triple::thumb: 98 if (m_exc_code == 1) 99 code_desc = "EXC_ARM_UNDEFINED"; 100 break; 101 102 default: 103 break; 104 } 105 break; 106 107 case 3: // EXC_ARITHMETIC 108 exc_desc = "EXC_ARITHMETIC"; 109 switch (cpu) { 110 case llvm::Triple::x86: 111 case llvm::Triple::x86_64: 112 switch (m_exc_code) { 113 case 1: 114 code_desc = "EXC_I386_DIV"; 115 break; 116 case 2: 117 code_desc = "EXC_I386_INTO"; 118 break; 119 case 3: 120 code_desc = "EXC_I386_NOEXT"; 121 break; 122 case 4: 123 code_desc = "EXC_I386_EXTOVR"; 124 break; 125 case 5: 126 code_desc = "EXC_I386_EXTERR"; 127 break; 128 case 6: 129 code_desc = "EXC_I386_EMERR"; 130 break; 131 case 7: 132 code_desc = "EXC_I386_BOUND"; 133 break; 134 case 8: 135 code_desc = "EXC_I386_SSEEXTERR"; 136 break; 137 } 138 break; 139 140 default: 141 break; 142 } 143 break; 144 145 case 4: // EXC_EMULATION 146 exc_desc = "EXC_EMULATION"; 147 break; 148 149 case 5: // EXC_SOFTWARE 150 exc_desc = "EXC_SOFTWARE"; 151 if (m_exc_code == 0x10003) { 152 subcode_desc = "EXC_SOFT_SIGNAL"; 153 subcode_label = "signo"; 154 } 155 break; 156 157 case 6: // EXC_BREAKPOINT 158 { 159 exc_desc = "EXC_BREAKPOINT"; 160 switch (cpu) { 161 case llvm::Triple::x86: 162 case llvm::Triple::x86_64: 163 switch (m_exc_code) { 164 case 1: 165 code_desc = "EXC_I386_SGL"; 166 break; 167 case 2: 168 code_desc = "EXC_I386_BPT"; 169 break; 170 } 171 break; 172 173 case llvm::Triple::arm: 174 case llvm::Triple::thumb: 175 switch (m_exc_code) { 176 case 0x101: 177 code_desc = "EXC_ARM_DA_ALIGN"; 178 break; 179 case 0x102: 180 code_desc = "EXC_ARM_DA_DEBUG"; 181 break; 182 case 1: 183 code_desc = "EXC_ARM_BREAKPOINT"; 184 break; 185 // FIXME temporary workaround, exc_code 0 does not really mean 186 // EXC_ARM_BREAKPOINT 187 case 0: 188 code_desc = "EXC_ARM_BREAKPOINT"; 189 break; 190 } 191 break; 192 193 default: 194 break; 195 } 196 } break; 197 198 case 7: 199 exc_desc = "EXC_SYSCALL"; 200 break; 201 202 case 8: 203 exc_desc = "EXC_MACH_SYSCALL"; 204 break; 205 206 case 9: 207 exc_desc = "EXC_RPC_ALERT"; 208 break; 209 210 case 10: 211 exc_desc = "EXC_CRASH"; 212 break; 213 case 11: 214 exc_desc = "EXC_RESOURCE"; 215 #if defined(__APPLE__) 216 { 217 int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code); 218 219 code_label = "limit"; 220 code_desc = code_desc_buf; 221 subcode_label = "observed"; 222 subcode_desc = subcode_desc_buf; 223 224 switch (resource_type) { 225 case RESOURCE_TYPE_CPU: 226 exc_desc = "EXC_RESOURCE RESOURCE_TYPE_CPU"; 227 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%", 228 (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code)); 229 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%", 230 (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED( 231 m_exc_subcode)); 232 break; 233 case RESOURCE_TYPE_WAKEUPS: 234 exc_desc = "EXC_RESOURCE RESOURCE_TYPE_WAKEUPS"; 235 snprintf( 236 code_desc_buf, sizeof(code_desc_buf), "%d w/s", 237 (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code)); 238 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s", 239 (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED( 240 m_exc_subcode)); 241 break; 242 case RESOURCE_TYPE_MEMORY: 243 exc_desc = "EXC_RESOURCE RESOURCE_TYPE_MEMORY"; 244 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB", 245 (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code)); 246 subcode_desc = nullptr; 247 subcode_label = "unused"; 248 break; 249 #if defined(RESOURCE_TYPE_IO) 250 // RESOURCE_TYPE_IO is introduced in macOS SDK 10.12. 251 case RESOURCE_TYPE_IO: 252 exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO"; 253 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB", 254 (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code)); 255 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB", 256 (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode)); 257 ; 258 break; 259 #endif 260 } 261 } 262 #endif 263 break; 264 case 12: 265 exc_desc = "EXC_GUARD"; 266 break; 267 } 268 269 StreamString strm; 270 271 if (exc_desc) 272 strm.PutCString(exc_desc); 273 else 274 strm.Printf("EXC_??? (%" PRIu64 ")", m_value); 275 276 if (m_exc_data_count >= 1) { 277 if (code_desc) 278 strm.Printf(" (%s=%s", code_label, code_desc); 279 else 280 strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code); 281 } 282 283 if (m_exc_data_count >= 2) { 284 if (subcode_desc) 285 strm.Printf(", %s=%s", subcode_label, subcode_desc); 286 else 287 strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode); 288 } 289 290 if (m_exc_data_count > 0) 291 strm.PutChar(')'); 292 293 m_description = std::string(strm.GetString()); 294 return m_description.c_str(); 295 } 296 297 static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target, 298 uint32_t exc_data_count, 299 uint64_t exc_sub_code, 300 uint64_t exc_sub_sub_code) { 301 // Try hardware watchpoint. 302 if (target) { 303 // The exc_sub_code indicates the data break address. 304 lldb::WatchpointSP wp_sp = 305 target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code); 306 if (wp_sp && wp_sp->IsEnabled()) { 307 // Debugserver may piggyback the hardware index of the fired watchpoint 308 // in the exception data. Set the hardware index if that's the case. 309 if (exc_data_count >= 3) 310 wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); 311 return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID()); 312 } 313 } 314 315 // Try hardware breakpoint. 316 ProcessSP process_sp(thread.GetProcess()); 317 if (process_sp) { 318 // The exc_sub_code indicates the data break address. 319 lldb::BreakpointSiteSP bp_sp = 320 process_sp->GetBreakpointSiteList().FindByAddress( 321 (lldb::addr_t)exc_sub_code); 322 if (bp_sp && bp_sp->IsEnabled()) { 323 // Debugserver may piggyback the hardware index of the fired breakpoint 324 // in the exception data. Set the hardware index if that's the case. 325 if (exc_data_count >= 3) 326 bp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); 327 return StopInfo::CreateStopReasonWithBreakpointSiteID(thread, 328 bp_sp->GetID()); 329 } 330 } 331 332 return nullptr; 333 } 334 335 StopInfoSP StopInfoMachException::CreateStopReasonWithMachException( 336 Thread &thread, uint32_t exc_type, uint32_t exc_data_count, 337 uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code, 338 bool pc_already_adjusted, bool adjust_pc_if_needed) { 339 if (exc_type == 0) 340 return StopInfoSP(); 341 342 uint32_t pc_decrement = 0; 343 ExecutionContext exe_ctx(thread.shared_from_this()); 344 Target *target = exe_ctx.GetTargetPtr(); 345 const llvm::Triple::ArchType cpu = 346 target ? target->GetArchitecture().GetMachine() 347 : llvm::Triple::UnknownArch; 348 349 switch (exc_type) { 350 case 1: // EXC_BAD_ACCESS 351 case 2: // EXC_BAD_INSTRUCTION 352 case 3: // EXC_ARITHMETIC 353 case 4: // EXC_EMULATION 354 break; 355 356 case 5: // EXC_SOFTWARE 357 if (exc_code == 0x10003) // EXC_SOFT_SIGNAL 358 { 359 if (exc_sub_code == 5) { 360 // On MacOSX, a SIGTRAP can signify that a process has called exec, 361 // so we should check with our dynamic loader to verify. 362 ProcessSP process_sp(thread.GetProcess()); 363 if (process_sp) { 364 DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader(); 365 if (dynamic_loader && dynamic_loader->ProcessDidExec()) { 366 // The program was re-exec'ed 367 return StopInfo::CreateStopReasonWithExec(thread); 368 } 369 } 370 } 371 return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code); 372 } 373 break; 374 375 case 6: // EXC_BREAKPOINT 376 { 377 bool is_actual_breakpoint = false; 378 bool is_trace_if_actual_breakpoint_missing = false; 379 switch (cpu) { 380 case llvm::Triple::x86: 381 case llvm::Triple::x86_64: 382 if (exc_code == 1) // EXC_I386_SGL 383 { 384 if (!exc_sub_code) { 385 // This looks like a plain trap. 386 // Have to check if there is a breakpoint here as well. When you 387 // single-step onto a trap, the single step stops you not to trap. 388 // Since we also do that check below, let's just use that logic. 389 is_actual_breakpoint = true; 390 is_trace_if_actual_breakpoint_missing = true; 391 } else { 392 if (StopInfoSP stop_info = 393 GetStopInfoForHardwareBP(thread, target, exc_data_count, 394 exc_sub_code, exc_sub_sub_code)) 395 return stop_info; 396 } 397 } else if (exc_code == 2 || // EXC_I386_BPT 398 exc_code == 3) // EXC_I386_BPTFLT 399 { 400 // KDP returns EXC_I386_BPTFLT for trace breakpoints 401 if (exc_code == 3) 402 is_trace_if_actual_breakpoint_missing = true; 403 404 is_actual_breakpoint = true; 405 if (!pc_already_adjusted) 406 pc_decrement = 1; 407 } 408 break; 409 410 case llvm::Triple::arm: 411 case llvm::Triple::thumb: 412 if (exc_code == 0x102) // EXC_ARM_DA_DEBUG 413 { 414 // It's a watchpoint, then, if the exc_sub_code indicates a 415 // known/enabled data break address from our watchpoint list. 416 lldb::WatchpointSP wp_sp; 417 if (target) 418 wp_sp = target->GetWatchpointList().FindByAddress( 419 (lldb::addr_t)exc_sub_code); 420 if (wp_sp && wp_sp->IsEnabled()) { 421 // Debugserver may piggyback the hardware index of the fired 422 // watchpoint in the exception data. Set the hardware index if 423 // that's the case. 424 if (exc_data_count >= 3) 425 wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); 426 return StopInfo::CreateStopReasonWithWatchpointID(thread, 427 wp_sp->GetID()); 428 } else { 429 is_actual_breakpoint = true; 430 is_trace_if_actual_breakpoint_missing = true; 431 } 432 } else if (exc_code == 1) // EXC_ARM_BREAKPOINT 433 { 434 is_actual_breakpoint = true; 435 is_trace_if_actual_breakpoint_missing = true; 436 } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel 437 // is currently returning this so accept it 438 // as indicating a breakpoint until the 439 // kernel is fixed 440 { 441 is_actual_breakpoint = true; 442 is_trace_if_actual_breakpoint_missing = true; 443 } 444 break; 445 446 case llvm::Triple::aarch64_32: 447 case llvm::Triple::aarch64: { 448 if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT 449 { 450 // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0 451 // is set 452 is_actual_breakpoint = false; 453 is_trace_if_actual_breakpoint_missing = true; 454 } 455 if (exc_code == 0x102) // EXC_ARM_DA_DEBUG 456 { 457 // It's a watchpoint, then, if the exc_sub_code indicates a 458 // known/enabled data break address from our watchpoint list. 459 lldb::WatchpointSP wp_sp; 460 if (target) 461 wp_sp = target->GetWatchpointList().FindByAddress( 462 (lldb::addr_t)exc_sub_code); 463 if (wp_sp && wp_sp->IsEnabled()) { 464 // Debugserver may piggyback the hardware index of the fired 465 // watchpoint in the exception data. Set the hardware index if 466 // that's the case. 467 if (exc_data_count >= 3) 468 wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); 469 return StopInfo::CreateStopReasonWithWatchpointID(thread, 470 wp_sp->GetID()); 471 } 472 // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as 473 // EXC_BAD_ACCESS 474 if (thread.GetTemporaryResumeState() == eStateStepping) 475 return StopInfo::CreateStopReasonToTrace(thread); 476 } 477 // It looks like exc_sub_code has the 4 bytes of the instruction that 478 // triggered the exception, i.e. our breakpoint opcode 479 is_actual_breakpoint = exc_code == 1; 480 break; 481 } 482 483 default: 484 break; 485 } 486 487 if (is_actual_breakpoint) { 488 RegisterContextSP reg_ctx_sp(thread.GetRegisterContext()); 489 addr_t pc = reg_ctx_sp->GetPC() - pc_decrement; 490 491 ProcessSP process_sp(thread.CalculateProcess()); 492 493 lldb::BreakpointSiteSP bp_site_sp; 494 if (process_sp) 495 bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc); 496 if (bp_site_sp && bp_site_sp->IsEnabled()) { 497 // Update the PC if we were asked to do so, but only do so if we find 498 // a breakpoint that we know about cause this could be a trap 499 // instruction in the code 500 if (pc_decrement > 0 && adjust_pc_if_needed) 501 reg_ctx_sp->SetPC(pc); 502 503 // If the breakpoint is for this thread, then we'll report the hit, 504 // but if it is for another thread, we can just report no reason. We 505 // don't need to worry about stepping over the breakpoint here, that 506 // will be taken care of when the thread resumes and notices that 507 // there's a breakpoint under the pc. If we have an operating system 508 // plug-in, we might have set a thread specific breakpoint using the 509 // operating system thread ID, so we can't make any assumptions about 510 // the thread ID so we must always report the breakpoint regardless 511 // of the thread. 512 if (bp_site_sp->ValidForThisThread(thread) || 513 thread.GetProcess()->GetOperatingSystem() != nullptr) 514 return StopInfo::CreateStopReasonWithBreakpointSiteID( 515 thread, bp_site_sp->GetID()); 516 else if (is_trace_if_actual_breakpoint_missing) 517 return StopInfo::CreateStopReasonToTrace(thread); 518 else 519 return StopInfoSP(); 520 } 521 522 // Don't call this a trace if we weren't single stepping this thread. 523 if (is_trace_if_actual_breakpoint_missing && 524 thread.GetTemporaryResumeState() == eStateStepping) { 525 return StopInfo::CreateStopReasonToTrace(thread); 526 } 527 } 528 } break; 529 530 case 7: // EXC_SYSCALL 531 case 8: // EXC_MACH_SYSCALL 532 case 9: // EXC_RPC_ALERT 533 case 10: // EXC_CRASH 534 break; 535 } 536 537 return StopInfoSP(new StopInfoMachException(thread, exc_type, exc_data_count, 538 exc_code, exc_sub_code)); 539 } 540