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