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/ABI.h"
21#include "lldb/Target/DynamicLoader.h"
22#include "lldb/Target/ExecutionContext.h"
23#include "lldb/Target/Process.h"
24#include "lldb/Target/RegisterContext.h"
25#include "lldb/Target/Target.h"
26#include "lldb/Target/Thread.h"
27#include "lldb/Target/ThreadPlan.h"
28#include "lldb/Target/UnixSignals.h"
29#include "lldb/Utility/LLDBLog.h"
30#include "lldb/Utility/Log.h"
31#include "lldb/Utility/StreamString.h"
32#include <optional>
33
34using namespace lldb;
35using namespace lldb_private;
36
37/// Information about a pointer-authentication related instruction.
38struct PtrauthInstructionInfo {
39 bool IsAuthenticated;
40 bool IsLoad;
41 bool DoesBranch;
42};
43
44/// Get any pointer-authentication related information about the instruction
45/// at address \p at_addr.
46static std::optional<PtrauthInstructionInfo>
47GetPtrauthInstructionInfo(Target &target, const ArchSpec &arch,
48 const Address &at_addr) {
49 const char *plugin_name = nullptr;
50 const char *flavor = nullptr;
51 AddressRange range_bounds(at_addr, 4);
52 const bool prefer_file_cache = true;
53 DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
54 arch, plugin_name, flavor, target, disasm_range: range_bounds, force_live_memory: prefer_file_cache);
55 if (!disassembler_sp)
56 return std::nullopt;
57
58 InstructionList &insn_list = disassembler_sp->GetInstructionList();
59 InstructionSP insn = insn_list.GetInstructionAtIndex(idx: 0);
60 if (!insn)
61 return std::nullopt;
62
63 return PtrauthInstructionInfo{.IsAuthenticated: insn->IsAuthenticated(), .IsLoad: insn->IsLoad(),
64 .DoesBranch: insn->DoesBranch()};
65}
66
67/// Describe the load address of \p addr using the format filename:line:col.
68static void DescribeAddressBriefly(Stream &strm, const Address &addr,
69 Target &target) {
70 strm.Printf(format: "at address=0x%" PRIx64, addr.GetLoadAddress(target: &target));
71 StreamString s;
72 if (addr.GetDescription(s, target, level: eDescriptionLevelBrief))
73 strm.Printf(format: " %s", s.GetString().data());
74 strm.Printf(format: ".\n");
75}
76
77bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) {
78 bool IsBreakpoint = m_value == 6; // EXC_BREAKPOINT
79 bool IsBadAccess = m_value == 1; // EXC_BAD_ACCESS
80 if (!IsBreakpoint && !IsBadAccess)
81 return false;
82
83 // Check that we have a live process.
84 if (!exe_ctx.HasProcessScope() || !exe_ctx.HasThreadScope() ||
85 !exe_ctx.HasTargetScope())
86 return false;
87
88 Thread &thread = *exe_ctx.GetThreadPtr();
89 StackFrameSP current_frame = thread.GetStackFrameAtIndex(idx: 0);
90 if (!current_frame)
91 return false;
92
93 Target &target = *exe_ctx.GetTargetPtr();
94 Process &process = *exe_ctx.GetProcessPtr();
95 ABISP abi_sp = process.GetABI();
96 const ArchSpec &arch = target.GetArchitecture();
97 assert(abi_sp && "Missing ABI info");
98
99 // Check for a ptrauth-enabled target.
100 const bool ptrauth_enabled_target =
101 arch.GetCore() == ArchSpec::eCore_arm_arm64e;
102 if (!ptrauth_enabled_target)
103 return false;
104
105 // Set up a stream we can write a diagnostic into.
106 StreamString strm;
107 auto emit_ptrauth_prologue = [&](uint64_t at_address) {
108 strm.Printf(format: "EXC_BAD_ACCESS (code=%" PRIu64 ", address=0x%" PRIx64 ")\n",
109 m_exc_code, at_address);
110 strm.Printf(format: "Note: Possible pointer authentication failure detected.\n");
111 };
112
113 // Check if we have a "brk 0xc47x" trap, where the value that failed to
114 // authenticate is in x16.
115 Address current_address = current_frame->GetFrameCodeAddress();
116 if (IsBreakpoint) {
117 RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
118 if (!reg_ctx)
119 return false;
120
121 const RegisterInfo *X16Info = reg_ctx->GetRegisterInfoByName(reg_name: "x16");
122 RegisterValue X16Val;
123 if (!reg_ctx->ReadRegister(reg_info: X16Info, reg_value&: X16Val))
124 return false;
125 uint64_t bad_address = X16Val.GetAsUInt64();
126
127 uint64_t fixed_bad_address = abi_sp->FixCodeAddress(pc: bad_address);
128 Address brk_address;
129 if (!target.ResolveLoadAddress(load_addr: fixed_bad_address, so_addr&: brk_address))
130 return false;
131
132 auto brk_ptrauth_info =
133 GetPtrauthInstructionInfo(target, arch, at_addr: current_address);
134 if (brk_ptrauth_info && brk_ptrauth_info->IsAuthenticated) {
135 emit_ptrauth_prologue(bad_address);
136 strm.Printf(format: "Found value that failed to authenticate ");
137 DescribeAddressBriefly(strm, addr: brk_address, target);
138 m_description = std::string(strm.GetString());
139 return true;
140 }
141 return false;
142 }
143
144 assert(IsBadAccess && "Handle EXC_BAD_ACCESS only after this point");
145
146 // Check that we have the "bad address" from an EXC_BAD_ACCESS.
147 if (m_exc_data_count < 2)
148 return false;
149
150 // Ok, we know the Target is valid and that it describes a ptrauth-enabled
151 // device. Now, we need to determine whether this exception was caused by a
152 // ptrauth failure.
153
154 uint64_t bad_address = m_exc_subcode;
155 uint64_t fixed_bad_address = abi_sp->FixCodeAddress(pc: bad_address);
156 uint64_t current_pc = current_address.GetLoadAddress(target: &target);
157
158 // Detect: LDRAA, LDRAB (Load Register, with pointer authentication).
159 //
160 // If an authenticated load results in an exception, the instruction at the
161 // current PC should be one of LDRAx.
162 if (bad_address != current_pc && fixed_bad_address != current_pc) {
163 auto ptrauth_info =
164 GetPtrauthInstructionInfo(target, arch, at_addr: current_address);
165 if (ptrauth_info && ptrauth_info->IsAuthenticated && ptrauth_info->IsLoad) {
166 emit_ptrauth_prologue(bad_address);
167 strm.Printf(format: "Found authenticated load instruction ");
168 DescribeAddressBriefly(strm, addr: current_address, target);
169 m_description = std::string(strm.GetString());
170 return true;
171 }
172 }
173
174 // Detect: BLRAA, BLRAAZ, BLRAB, BLRABZ (Branch with Link to Register, with
175 // pointer authentication).
176 //
177 // TODO: Detect: BRAA, BRAAZ, BRAB, BRABZ (Branch to Register, with pointer
178 // authentication). At a minimum, this requires call site info support for
179 // indirect calls.
180 //
181 // If an authenticated call or tail call results in an exception, stripping
182 // the bad address should give the current PC, which points to the address
183 // we tried to branch to.
184 if (bad_address != current_pc && fixed_bad_address == current_pc) {
185 if (StackFrameSP parent_frame = thread.GetStackFrameAtIndex(idx: 1)) {
186 addr_t return_pc =
187 parent_frame->GetFrameCodeAddress().GetLoadAddress(target: &target);
188 Address blr_address;
189 if (!target.ResolveLoadAddress(load_addr: return_pc - 4, so_addr&: blr_address))
190 return false;
191
192 auto blr_ptrauth_info =
193 GetPtrauthInstructionInfo(target, arch, at_addr: blr_address);
194 if (blr_ptrauth_info && blr_ptrauth_info->IsAuthenticated &&
195 blr_ptrauth_info->DoesBranch) {
196 emit_ptrauth_prologue(bad_address);
197 strm.Printf(format: "Found authenticated indirect branch ");
198 DescribeAddressBriefly(strm, addr: blr_address, target);
199 m_description = std::string(strm.GetString());
200 return true;
201 }
202 }
203 }
204
205 // TODO: Detect: RETAA, RETAB (Return from subroutine, with pointer
206 // authentication).
207 //
208 // Is there a motivating, non-malicious code snippet that corrupts LR?
209
210 return false;
211}
212
213const char *StopInfoMachException::GetDescription() {
214 if (!m_description.empty())
215 return m_description.c_str();
216 if (GetValue() == eStopReasonInvalid)
217 return "invalid stop reason!";
218
219 ExecutionContext exe_ctx(m_thread_wp.lock());
220 Target *target = exe_ctx.GetTargetPtr();
221 const llvm::Triple::ArchType cpu =
222 target ? target->GetArchitecture().GetMachine()
223 : llvm::Triple::UnknownArch;
224
225 const char *exc_desc = nullptr;
226 const char *code_label = "code";
227 const char *code_desc = nullptr;
228 const char *subcode_label = "subcode";
229 const char *subcode_desc = nullptr;
230
231#if defined(__APPLE__)
232 char code_desc_buf[32];
233 char subcode_desc_buf[32];
234#endif
235
236 switch (m_value) {
237 case 1: // EXC_BAD_ACCESS
238 exc_desc = "EXC_BAD_ACCESS";
239 subcode_label = "address";
240 switch (cpu) {
241 case llvm::Triple::x86:
242 case llvm::Triple::x86_64:
243 switch (m_exc_code) {
244 case 0xd:
245 code_desc = "EXC_I386_GPFLT";
246 m_exc_data_count = 1;
247 break;
248 }
249 break;
250 case llvm::Triple::arm:
251 case llvm::Triple::thumb:
252 switch (m_exc_code) {
253 case 0x101:
254 code_desc = "EXC_ARM_DA_ALIGN";
255 break;
256 case 0x102:
257 code_desc = "EXC_ARM_DA_DEBUG";
258 break;
259 }
260 break;
261
262 case llvm::Triple::aarch64:
263 if (DeterminePtrauthFailure(exe_ctx))
264 return m_description.c_str();
265 break;
266
267 default:
268 break;
269 }
270 break;
271
272 case 2: // EXC_BAD_INSTRUCTION
273 exc_desc = "EXC_BAD_INSTRUCTION";
274 switch (cpu) {
275 case llvm::Triple::x86:
276 case llvm::Triple::x86_64:
277 if (m_exc_code == 1)
278 code_desc = "EXC_I386_INVOP";
279 break;
280
281 case llvm::Triple::arm:
282 case llvm::Triple::thumb:
283 if (m_exc_code == 1)
284 code_desc = "EXC_ARM_UNDEFINED";
285 break;
286
287 default:
288 break;
289 }
290 break;
291
292 case 3: // EXC_ARITHMETIC
293 exc_desc = "EXC_ARITHMETIC";
294 switch (cpu) {
295 case llvm::Triple::x86:
296 case llvm::Triple::x86_64:
297 switch (m_exc_code) {
298 case 1:
299 code_desc = "EXC_I386_DIV";
300 break;
301 case 2:
302 code_desc = "EXC_I386_INTO";
303 break;
304 case 3:
305 code_desc = "EXC_I386_NOEXT";
306 break;
307 case 4:
308 code_desc = "EXC_I386_EXTOVR";
309 break;
310 case 5:
311 code_desc = "EXC_I386_EXTERR";
312 break;
313 case 6:
314 code_desc = "EXC_I386_EMERR";
315 break;
316 case 7:
317 code_desc = "EXC_I386_BOUND";
318 break;
319 case 8:
320 code_desc = "EXC_I386_SSEEXTERR";
321 break;
322 }
323 break;
324
325 default:
326 break;
327 }
328 break;
329
330 case 4: // EXC_EMULATION
331 exc_desc = "EXC_EMULATION";
332 break;
333
334 case 5: // EXC_SOFTWARE
335 exc_desc = "EXC_SOFTWARE";
336 if (m_exc_code == 0x10003) {
337 subcode_desc = "EXC_SOFT_SIGNAL";
338 subcode_label = "signo";
339 }
340 break;
341
342 case 6: // EXC_BREAKPOINT
343 {
344 exc_desc = "EXC_BREAKPOINT";
345 switch (cpu) {
346 case llvm::Triple::x86:
347 case llvm::Triple::x86_64:
348 switch (m_exc_code) {
349 case 1:
350 code_desc = "EXC_I386_SGL";
351 break;
352 case 2:
353 code_desc = "EXC_I386_BPT";
354 break;
355 }
356 break;
357
358 case llvm::Triple::arm:
359 case llvm::Triple::thumb:
360 switch (m_exc_code) {
361 case 0x101:
362 code_desc = "EXC_ARM_DA_ALIGN";
363 break;
364 case 0x102:
365 code_desc = "EXC_ARM_DA_DEBUG";
366 break;
367 case 1:
368 code_desc = "EXC_ARM_BREAKPOINT";
369 break;
370 // FIXME temporary workaround, exc_code 0 does not really mean
371 // EXC_ARM_BREAKPOINT
372 case 0:
373 code_desc = "EXC_ARM_BREAKPOINT";
374 break;
375 }
376 break;
377
378 case llvm::Triple::aarch64:
379 if (DeterminePtrauthFailure(exe_ctx))
380 return m_description.c_str();
381 break;
382
383 default:
384 break;
385 }
386 } break;
387
388 case 7:
389 exc_desc = "EXC_SYSCALL";
390 break;
391
392 case 8:
393 exc_desc = "EXC_MACH_SYSCALL";
394 break;
395
396 case 9:
397 exc_desc = "EXC_RPC_ALERT";
398 break;
399
400 case 10:
401 exc_desc = "EXC_CRASH";
402 break;
403 case 11:
404 exc_desc = "EXC_RESOURCE";
405#if defined(__APPLE__)
406 {
407 int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code);
408
409 code_label = "limit";
410 code_desc = code_desc_buf;
411 subcode_label = "observed";
412 subcode_desc = subcode_desc_buf;
413
414 switch (resource_type) {
415 case RESOURCE_TYPE_CPU:
416 exc_desc =
417 "EXC_RESOURCE (RESOURCE_TYPE_CPU: CPU usage monitor tripped)";
418 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%",
419 (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code));
420 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%",
421 (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED(
422 m_exc_subcode));
423 break;
424 case RESOURCE_TYPE_WAKEUPS:
425 exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_WAKEUPS: idle wakeups monitor "
426 "tripped)";
427 snprintf(
428 code_desc_buf, sizeof(code_desc_buf), "%d w/s",
429 (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code));
430 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s",
431 (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED(
432 m_exc_subcode));
433 break;
434 case RESOURCE_TYPE_MEMORY:
435 exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory "
436 "limit exceeded)";
437 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
438 (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code));
439 subcode_desc = nullptr;
440 subcode_label = nullptr;
441 break;
442#if defined(RESOURCE_TYPE_IO)
443 // RESOURCE_TYPE_IO is introduced in macOS SDK 10.12.
444 case RESOURCE_TYPE_IO:
445 exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO";
446 snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
447 (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code));
448 snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB",
449 (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode));
450 ;
451 break;
452#endif
453 }
454 }
455#endif
456 break;
457 case 12:
458 exc_desc = "EXC_GUARD";
459 break;
460 }
461
462 StreamString strm;
463
464 if (exc_desc)
465 strm.PutCString(cstr: exc_desc);
466 else
467 strm.Printf(format: "EXC_??? (%" PRIu64 ")", m_value);
468
469 if (m_exc_data_count >= 1) {
470 if (code_desc)
471 strm.Printf(format: " (%s=%s", code_label, code_desc);
472 else
473 strm.Printf(format: " (%s=%" PRIu64, code_label, m_exc_code);
474 }
475
476 if (m_exc_data_count >= 2) {
477 if (subcode_label && subcode_desc)
478 strm.Printf(format: ", %s=%s", subcode_label, subcode_desc);
479 else if (subcode_label)
480 strm.Printf(format: ", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
481 }
482
483 if (m_exc_data_count > 0)
484 strm.PutChar(ch: ')');
485
486 m_description = std::string(strm.GetString());
487 return m_description.c_str();
488}
489
490static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target,
491 uint32_t exc_data_count,
492 uint64_t exc_sub_code,
493 uint64_t exc_sub_sub_code) {
494 // Try hardware watchpoint.
495 if (target) {
496 // The exc_sub_code indicates the data break address.
497 WatchpointResourceSP wp_rsrc_sp =
498 target->GetProcessSP()->GetWatchpointResourceList().FindByAddress(
499 addr: (addr_t)exc_sub_code);
500 if (wp_rsrc_sp && wp_rsrc_sp->GetNumberOfConstituents() > 0) {
501 return StopInfo::CreateStopReasonWithWatchpointID(
502 thread, watch_id: wp_rsrc_sp->GetConstituentAtIndex(idx: 0)->GetID());
503 }
504 }
505
506 // Try hardware breakpoint.
507 ProcessSP process_sp(thread.GetProcess());
508 if (process_sp) {
509 // The exc_sub_code indicates the data break address.
510 lldb::BreakpointSiteSP bp_sp =
511 process_sp->GetBreakpointSiteList().FindByAddress(
512 addr: (lldb::addr_t)exc_sub_code);
513 if (bp_sp && bp_sp->IsEnabled()) {
514 return StopInfo::CreateStopReasonWithBreakpointSiteID(thread,
515 break_id: bp_sp->GetID());
516 }
517 }
518
519 return nullptr;
520}
521
522#if defined(__APPLE__)
523const char *
524StopInfoMachException::MachException::Name(exception_type_t exc_type) {
525 switch (exc_type) {
526 case EXC_BAD_ACCESS:
527 return "EXC_BAD_ACCESS";
528 case EXC_BAD_INSTRUCTION:
529 return "EXC_BAD_INSTRUCTION";
530 case EXC_ARITHMETIC:
531 return "EXC_ARITHMETIC";
532 case EXC_EMULATION:
533 return "EXC_EMULATION";
534 case EXC_SOFTWARE:
535 return "EXC_SOFTWARE";
536 case EXC_BREAKPOINT:
537 return "EXC_BREAKPOINT";
538 case EXC_SYSCALL:
539 return "EXC_SYSCALL";
540 case EXC_MACH_SYSCALL:
541 return "EXC_MACH_SYSCALL";
542 case EXC_RPC_ALERT:
543 return "EXC_RPC_ALERT";
544#ifdef EXC_CRASH
545 case EXC_CRASH:
546 return "EXC_CRASH";
547#endif
548 case EXC_RESOURCE:
549 return "EXC_RESOURCE";
550#ifdef EXC_GUARD
551 case EXC_GUARD:
552 return "EXC_GUARD";
553#endif
554#ifdef EXC_CORPSE_NOTIFY
555 case EXC_CORPSE_NOTIFY:
556 return "EXC_CORPSE_NOTIFY";
557#endif
558#ifdef EXC_CORPSE_VARIANT_BIT
559 case EXC_CORPSE_VARIANT_BIT:
560 return "EXC_CORPSE_VARIANT_BIT";
561#endif
562 default:
563 break;
564 }
565 return NULL;
566}
567
568std::optional<exception_type_t>
569StopInfoMachException::MachException::ExceptionCode(const char *name) {
570 return llvm::StringSwitch<std::optional<exception_type_t>>(name)
571 .Case("EXC_BAD_ACCESS", EXC_BAD_ACCESS)
572 .Case("EXC_BAD_INSTRUCTION", EXC_BAD_INSTRUCTION)
573 .Case("EXC_ARITHMETIC", EXC_ARITHMETIC)
574 .Case("EXC_EMULATION", EXC_EMULATION)
575 .Case("EXC_SOFTWARE", EXC_SOFTWARE)
576 .Case("EXC_BREAKPOINT", EXC_BREAKPOINT)
577 .Case("EXC_SYSCALL", EXC_SYSCALL)
578 .Case("EXC_MACH_SYSCALL", EXC_MACH_SYSCALL)
579 .Case("EXC_RPC_ALERT", EXC_RPC_ALERT)
580#ifdef EXC_CRASH
581 .Case("EXC_CRASH", EXC_CRASH)
582#endif
583 .Case("EXC_RESOURCE", EXC_RESOURCE)
584#ifdef EXC_GUARD
585 .Case("EXC_GUARD", EXC_GUARD)
586#endif
587#ifdef EXC_CORPSE_NOTIFY
588 .Case("EXC_CORPSE_NOTIFY", EXC_CORPSE_NOTIFY)
589#endif
590 .Default(std::nullopt);
591}
592#endif
593
594StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
595 Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
596 uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
597 bool pc_already_adjusted, bool adjust_pc_if_needed) {
598 if (exc_type == 0)
599 return StopInfoSP();
600
601 bool not_stepping_but_got_singlestep_exception = false;
602 uint32_t pc_decrement = 0;
603 ExecutionContext exe_ctx(thread.shared_from_this());
604 Target *target = exe_ctx.GetTargetPtr();
605 const llvm::Triple::ArchType cpu =
606 target ? target->GetArchitecture().GetMachine()
607 : llvm::Triple::UnknownArch;
608
609 switch (exc_type) {
610 case 1: // EXC_BAD_ACCESS
611 case 2: // EXC_BAD_INSTRUCTION
612 case 3: // EXC_ARITHMETIC
613 case 4: // EXC_EMULATION
614 break;
615
616 case 5: // EXC_SOFTWARE
617 if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
618 {
619 if (exc_sub_code == 5) {
620 // On MacOSX, a SIGTRAP can signify that a process has called exec,
621 // so we should check with our dynamic loader to verify.
622 ProcessSP process_sp(thread.GetProcess());
623 if (process_sp) {
624 DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
625 if (dynamic_loader && dynamic_loader->ProcessDidExec()) {
626 // The program was re-exec'ed
627 return StopInfo::CreateStopReasonWithExec(thread);
628 }
629 }
630 }
631 return StopInfo::CreateStopReasonWithSignal(thread, signo: exc_sub_code);
632 }
633 break;
634
635 case 6: // EXC_BREAKPOINT
636 {
637 bool is_actual_breakpoint = false;
638 bool is_trace_if_actual_breakpoint_missing = false;
639 switch (cpu) {
640 case llvm::Triple::x86:
641 case llvm::Triple::x86_64:
642 if (exc_code == 1) // EXC_I386_SGL
643 {
644 if (!exc_sub_code) {
645 // This looks like a plain trap.
646 // Have to check if there is a breakpoint here as well. When you
647 // single-step onto a trap, the single step stops you not to trap.
648 // Since we also do that check below, let's just use that logic.
649 is_actual_breakpoint = true;
650 is_trace_if_actual_breakpoint_missing = true;
651 } else {
652 if (StopInfoSP stop_info =
653 GetStopInfoForHardwareBP(thread, target, exc_data_count,
654 exc_sub_code, exc_sub_sub_code))
655 return stop_info;
656 }
657 } else if (exc_code == 2 || // EXC_I386_BPT
658 exc_code == 3) // EXC_I386_BPTFLT
659 {
660 // KDP returns EXC_I386_BPTFLT for trace breakpoints
661 if (exc_code == 3)
662 is_trace_if_actual_breakpoint_missing = true;
663
664 is_actual_breakpoint = true;
665 if (!pc_already_adjusted)
666 pc_decrement = 1;
667 }
668 break;
669
670 case llvm::Triple::arm:
671 case llvm::Triple::thumb:
672 if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
673 {
674 // LWP_TODO: We need to find the WatchpointResource that matches
675 // the address, and evaluate its Watchpoints.
676
677 // It's a watchpoint, then, if the exc_sub_code indicates a
678 // known/enabled data break address from our watchpoint list.
679 lldb::WatchpointSP wp_sp;
680 if (target)
681 wp_sp = target->GetWatchpointList().FindByAddress(
682 addr: (lldb::addr_t)exc_sub_code);
683 if (wp_sp && wp_sp->IsEnabled()) {
684 return StopInfo::CreateStopReasonWithWatchpointID(thread,
685 watch_id: wp_sp->GetID());
686 } else {
687 is_actual_breakpoint = true;
688 is_trace_if_actual_breakpoint_missing = true;
689 }
690 } else if (exc_code == 1) // EXC_ARM_BREAKPOINT
691 {
692 is_actual_breakpoint = true;
693 is_trace_if_actual_breakpoint_missing = true;
694 } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel
695 // is currently returning this so accept it
696 // as indicating a breakpoint until the
697 // kernel is fixed
698 {
699 is_actual_breakpoint = true;
700 is_trace_if_actual_breakpoint_missing = true;
701 }
702 break;
703
704 case llvm::Triple::aarch64_32:
705 case llvm::Triple::aarch64: {
706 // xnu describes three things with type EXC_BREAKPOINT:
707 //
708 // exc_code 0x102 [EXC_ARM_DA_DEBUG], exc_sub_code addr-of-insn
709 // Watchpoint access. exc_sub_code is the address of the
710 // instruction which trigged the watchpoint trap.
711 // debugserver may add the watchpoint number that was triggered
712 // in exc_sub_sub_code.
713 //
714 // exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code 0
715 // Instruction step has completed.
716 //
717 // exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code address-of-instruction
718 // Software breakpoint instruction executed.
719
720 if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT
721 {
722 // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0
723 // is set
724 is_actual_breakpoint = true;
725 is_trace_if_actual_breakpoint_missing = true;
726 if (thread.GetTemporaryResumeState() != eStateStepping)
727 not_stepping_but_got_singlestep_exception = true;
728 }
729 if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
730 {
731 // LWP_TODO: We need to find the WatchpointResource that matches
732 // the address, and evaluate its Watchpoints.
733
734 // It's a watchpoint, then, if the exc_sub_code indicates a
735 // known/enabled data break address from our watchpoint list.
736 lldb::WatchpointSP wp_sp;
737 if (target)
738 wp_sp = target->GetWatchpointList().FindByAddress(
739 addr: (lldb::addr_t)exc_sub_code);
740 if (wp_sp && wp_sp->IsEnabled()) {
741 return StopInfo::CreateStopReasonWithWatchpointID(thread,
742 watch_id: wp_sp->GetID());
743 }
744 // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as
745 // EXC_BAD_ACCESS
746 if (thread.GetTemporaryResumeState() == eStateStepping)
747 return StopInfo::CreateStopReasonToTrace(thread);
748 }
749 // It looks like exc_sub_code has the 4 bytes of the instruction that
750 // triggered the exception, i.e. our breakpoint opcode
751 is_actual_breakpoint = exc_code == 1;
752 break;
753 }
754
755 default:
756 break;
757 }
758
759 if (is_actual_breakpoint) {
760 RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
761 addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
762
763 ProcessSP process_sp(thread.CalculateProcess());
764
765 lldb::BreakpointSiteSP bp_site_sp;
766 if (process_sp)
767 bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(addr: pc);
768 if (bp_site_sp && bp_site_sp->IsEnabled()) {
769 // Update the PC if we were asked to do so, but only do so if we find
770 // a breakpoint that we know about cause this could be a trap
771 // instruction in the code
772 if (pc_decrement > 0 && adjust_pc_if_needed)
773 reg_ctx_sp->SetPC(pc);
774
775 // If the breakpoint is for this thread, then we'll report the hit,
776 // but if it is for another thread, we can just report no reason. We
777 // don't need to worry about stepping over the breakpoint here, that
778 // will be taken care of when the thread resumes and notices that
779 // there's a breakpoint under the pc. If we have an operating system
780 // plug-in, we might have set a thread specific breakpoint using the
781 // operating system thread ID, so we can't make any assumptions about
782 // the thread ID so we must always report the breakpoint regardless
783 // of the thread.
784 if (bp_site_sp->ValidForThisThread(thread) ||
785 thread.GetProcess()->GetOperatingSystem() != nullptr)
786 return StopInfo::CreateStopReasonWithBreakpointSiteID(
787 thread, break_id: bp_site_sp->GetID());
788 else if (is_trace_if_actual_breakpoint_missing)
789 return StopInfo::CreateStopReasonToTrace(thread);
790 else
791 return StopInfoSP();
792 }
793
794 // Don't call this a trace if we weren't single stepping this thread.
795 if (is_trace_if_actual_breakpoint_missing &&
796 thread.GetTemporaryResumeState() == eStateStepping) {
797 return StopInfo::CreateStopReasonToTrace(thread);
798 }
799 }
800 } break;
801
802 case 7: // EXC_SYSCALL
803 case 8: // EXC_MACH_SYSCALL
804 case 9: // EXC_RPC_ALERT
805 case 10: // EXC_CRASH
806 break;
807 }
808
809 return std::make_shared<StopInfoMachException>(
810 args&: thread, args&: exc_type, args&: exc_data_count, args&: exc_code, args&: exc_sub_code,
811 args&: not_stepping_but_got_singlestep_exception);
812}
813
814// Detect an unusual situation on Darwin where:
815//
816// 0. We did an instruction-step before this.
817// 1. We have a hardware breakpoint or watchpoint set.
818// 2. We resumed the process, but not with an instruction-step.
819// 3. The thread gets an "instruction-step completed" mach exception.
820// 4. The pc has not advanced - it is the same as before.
821//
822// This method returns true for that combination of events.
823bool StopInfoMachException::WasContinueInterrupted(Thread &thread) {
824 Log *log = GetLog(mask: LLDBLog::Step);
825
826 // We got an instruction-step completed mach exception but we were not
827 // doing an instruction step on this thread.
828 if (!m_not_stepping_but_got_singlestep_exception)
829 return false;
830
831 RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
832 std::optional<addr_t> prev_pc = thread.GetPreviousFrameZeroPC();
833 if (!reg_ctx_sp || !prev_pc)
834 return false;
835
836 // The previous pc value and current pc value are the same.
837 if (*prev_pc != reg_ctx_sp->GetPC())
838 return false;
839
840 // We have a watchpoint -- this is the kernel bug.
841 ProcessSP process_sp = thread.GetProcess();
842 if (process_sp->GetWatchpointResourceList().GetSize()) {
843 LLDB_LOGF(log,
844 "Thread stopped with insn-step completed mach exception but "
845 "thread was not stepping; there is a hardware watchpoint set.");
846 return true;
847 }
848
849 // We have a hardware breakpoint -- this is the kernel bug.
850 auto &bp_site_list = process_sp->GetBreakpointSiteList();
851 for (auto &site : bp_site_list.Sites()) {
852 if (site->IsHardware() && site->IsEnabled()) {
853 LLDB_LOGF(log,
854 "Thread stopped with insn-step completed mach exception but "
855 "thread was not stepping; there is a hardware breakpoint set.");
856 return true;
857 }
858 }
859
860 return false;
861}
862

source code of lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp