xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- StopInfoMachException.cpp -----------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "StopInfoMachException.h"
100b57cec5SDimitry Andric 
115ffd83dbSDimitry Andric #include "lldb/lldb-forward.h"
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #if defined(__APPLE__)
140b57cec5SDimitry Andric // Needed for the EXC_RESOURCE interpretation macros
150b57cec5SDimitry Andric #include <kern/exc_resource.h>
160b57cec5SDimitry Andric #endif
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric #include "lldb/Breakpoint/Watchpoint.h"
190b57cec5SDimitry Andric #include "lldb/Symbol/Symbol.h"
20349cc55cSDimitry Andric #include "lldb/Target/ABI.h"
210b57cec5SDimitry Andric #include "lldb/Target/DynamicLoader.h"
220b57cec5SDimitry Andric #include "lldb/Target/ExecutionContext.h"
230b57cec5SDimitry Andric #include "lldb/Target/Process.h"
240b57cec5SDimitry Andric #include "lldb/Target/RegisterContext.h"
250b57cec5SDimitry Andric #include "lldb/Target/Target.h"
260b57cec5SDimitry Andric #include "lldb/Target/Thread.h"
270b57cec5SDimitry Andric #include "lldb/Target/ThreadPlan.h"
280b57cec5SDimitry Andric #include "lldb/Target/UnixSignals.h"
29*0fca6ea1SDimitry Andric #include "lldb/Utility/LLDBLog.h"
30*0fca6ea1SDimitry Andric #include "lldb/Utility/Log.h"
310b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
32bdd1243dSDimitry Andric #include <optional>
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric using namespace lldb;
350b57cec5SDimitry Andric using namespace lldb_private;
360b57cec5SDimitry Andric 
37349cc55cSDimitry Andric /// Information about a pointer-authentication related instruction.
38349cc55cSDimitry Andric struct PtrauthInstructionInfo {
39349cc55cSDimitry Andric   bool IsAuthenticated;
40349cc55cSDimitry Andric   bool IsLoad;
41349cc55cSDimitry Andric   bool DoesBranch;
42349cc55cSDimitry Andric };
43349cc55cSDimitry Andric 
44349cc55cSDimitry Andric /// Get any pointer-authentication related information about the instruction
45349cc55cSDimitry Andric /// at address \p at_addr.
46bdd1243dSDimitry Andric static std::optional<PtrauthInstructionInfo>
47349cc55cSDimitry Andric GetPtrauthInstructionInfo(Target &target, const ArchSpec &arch,
48349cc55cSDimitry Andric                           const Address &at_addr) {
49349cc55cSDimitry Andric   const char *plugin_name = nullptr;
50349cc55cSDimitry Andric   const char *flavor = nullptr;
51349cc55cSDimitry Andric   AddressRange range_bounds(at_addr, 4);
52349cc55cSDimitry Andric   const bool prefer_file_cache = true;
53349cc55cSDimitry Andric   DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
54349cc55cSDimitry Andric       arch, plugin_name, flavor, target, range_bounds, prefer_file_cache);
55349cc55cSDimitry Andric   if (!disassembler_sp)
56bdd1243dSDimitry Andric     return std::nullopt;
57349cc55cSDimitry Andric 
58349cc55cSDimitry Andric   InstructionList &insn_list = disassembler_sp->GetInstructionList();
59349cc55cSDimitry Andric   InstructionSP insn = insn_list.GetInstructionAtIndex(0);
60349cc55cSDimitry Andric   if (!insn)
61bdd1243dSDimitry Andric     return std::nullopt;
62349cc55cSDimitry Andric 
63349cc55cSDimitry Andric   return PtrauthInstructionInfo{insn->IsAuthenticated(), insn->IsLoad(),
64349cc55cSDimitry Andric                                 insn->DoesBranch()};
65349cc55cSDimitry Andric }
66349cc55cSDimitry Andric 
67349cc55cSDimitry Andric /// Describe the load address of \p addr using the format filename:line:col.
68349cc55cSDimitry Andric static void DescribeAddressBriefly(Stream &strm, const Address &addr,
69349cc55cSDimitry Andric                                    Target &target) {
70349cc55cSDimitry Andric   strm.Printf("at address=0x%" PRIx64, addr.GetLoadAddress(&target));
71349cc55cSDimitry Andric   StreamString s;
72349cc55cSDimitry Andric   if (addr.GetDescription(s, target, eDescriptionLevelBrief))
73349cc55cSDimitry Andric     strm.Printf(" %s", s.GetString().data());
74349cc55cSDimitry Andric   strm.Printf(".\n");
75349cc55cSDimitry Andric }
76349cc55cSDimitry Andric 
77349cc55cSDimitry Andric bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) {
78349cc55cSDimitry Andric   bool IsBreakpoint = m_value == 6; // EXC_BREAKPOINT
79349cc55cSDimitry Andric   bool IsBadAccess = m_value == 1;  // EXC_BAD_ACCESS
80349cc55cSDimitry Andric   if (!IsBreakpoint && !IsBadAccess)
81349cc55cSDimitry Andric     return false;
82349cc55cSDimitry Andric 
83349cc55cSDimitry Andric   // Check that we have a live process.
84349cc55cSDimitry Andric   if (!exe_ctx.HasProcessScope() || !exe_ctx.HasThreadScope() ||
85349cc55cSDimitry Andric       !exe_ctx.HasTargetScope())
86349cc55cSDimitry Andric     return false;
87349cc55cSDimitry Andric 
88349cc55cSDimitry Andric   Thread &thread = *exe_ctx.GetThreadPtr();
89349cc55cSDimitry Andric   StackFrameSP current_frame = thread.GetStackFrameAtIndex(0);
90349cc55cSDimitry Andric   if (!current_frame)
91349cc55cSDimitry Andric     return false;
92349cc55cSDimitry Andric 
93349cc55cSDimitry Andric   Target &target = *exe_ctx.GetTargetPtr();
94349cc55cSDimitry Andric   Process &process = *exe_ctx.GetProcessPtr();
95349cc55cSDimitry Andric   const ArchSpec &arch = target.GetArchitecture();
96349cc55cSDimitry Andric 
97349cc55cSDimitry Andric   // Check for a ptrauth-enabled target.
98349cc55cSDimitry Andric   const bool ptrauth_enabled_target =
99349cc55cSDimitry Andric       arch.GetCore() == ArchSpec::eCore_arm_arm64e;
100349cc55cSDimitry Andric   if (!ptrauth_enabled_target)
101349cc55cSDimitry Andric     return false;
102349cc55cSDimitry Andric 
103349cc55cSDimitry Andric   // Set up a stream we can write a diagnostic into.
104349cc55cSDimitry Andric   StreamString strm;
105349cc55cSDimitry Andric   auto emit_ptrauth_prologue = [&](uint64_t at_address) {
106349cc55cSDimitry Andric     strm.Printf("EXC_BAD_ACCESS (code=%" PRIu64 ", address=0x%" PRIx64 ")\n",
107349cc55cSDimitry Andric                 m_exc_code, at_address);
108349cc55cSDimitry Andric     strm.Printf("Note: Possible pointer authentication failure detected.\n");
109349cc55cSDimitry Andric   };
110349cc55cSDimitry Andric 
111*0fca6ea1SDimitry Andric   ABISP abi_sp = process.GetABI();
112*0fca6ea1SDimitry Andric   assert(abi_sp && "Missing ABI info");
113*0fca6ea1SDimitry Andric 
114349cc55cSDimitry Andric   // Check if we have a "brk 0xc47x" trap, where the value that failed to
115349cc55cSDimitry Andric   // authenticate is in x16.
116349cc55cSDimitry Andric   Address current_address = current_frame->GetFrameCodeAddress();
117349cc55cSDimitry Andric   if (IsBreakpoint) {
118349cc55cSDimitry Andric     RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
119349cc55cSDimitry Andric     if (!reg_ctx)
120349cc55cSDimitry Andric       return false;
121349cc55cSDimitry Andric 
122349cc55cSDimitry Andric     const RegisterInfo *X16Info = reg_ctx->GetRegisterInfoByName("x16");
123349cc55cSDimitry Andric     RegisterValue X16Val;
124349cc55cSDimitry Andric     if (!reg_ctx->ReadRegister(X16Info, X16Val))
125349cc55cSDimitry Andric       return false;
126349cc55cSDimitry Andric     uint64_t bad_address = X16Val.GetAsUInt64();
127349cc55cSDimitry Andric 
128349cc55cSDimitry Andric     uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
129349cc55cSDimitry Andric     Address brk_address;
130349cc55cSDimitry Andric     if (!target.ResolveLoadAddress(fixed_bad_address, brk_address))
131349cc55cSDimitry Andric       return false;
132349cc55cSDimitry Andric 
133349cc55cSDimitry Andric     auto brk_ptrauth_info =
134349cc55cSDimitry Andric         GetPtrauthInstructionInfo(target, arch, current_address);
135349cc55cSDimitry Andric     if (brk_ptrauth_info && brk_ptrauth_info->IsAuthenticated) {
136349cc55cSDimitry Andric       emit_ptrauth_prologue(bad_address);
137349cc55cSDimitry Andric       strm.Printf("Found value that failed to authenticate ");
138349cc55cSDimitry Andric       DescribeAddressBriefly(strm, brk_address, target);
139349cc55cSDimitry Andric       m_description = std::string(strm.GetString());
140349cc55cSDimitry Andric       return true;
141349cc55cSDimitry Andric     }
142349cc55cSDimitry Andric     return false;
143349cc55cSDimitry Andric   }
144349cc55cSDimitry Andric 
145349cc55cSDimitry Andric   assert(IsBadAccess && "Handle EXC_BAD_ACCESS only after this point");
146349cc55cSDimitry Andric 
147349cc55cSDimitry Andric   // Check that we have the "bad address" from an EXC_BAD_ACCESS.
148349cc55cSDimitry Andric   if (m_exc_data_count < 2)
149349cc55cSDimitry Andric     return false;
150349cc55cSDimitry Andric 
151349cc55cSDimitry Andric   // Ok, we know the Target is valid and that it describes a ptrauth-enabled
152349cc55cSDimitry Andric   // device. Now, we need to determine whether this exception was caused by a
153349cc55cSDimitry Andric   // ptrauth failure.
154349cc55cSDimitry Andric 
155349cc55cSDimitry Andric   uint64_t bad_address = m_exc_subcode;
156349cc55cSDimitry Andric   uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
157349cc55cSDimitry Andric   uint64_t current_pc = current_address.GetLoadAddress(&target);
158349cc55cSDimitry Andric 
159349cc55cSDimitry Andric   // Detect: LDRAA, LDRAB (Load Register, with pointer authentication).
160349cc55cSDimitry Andric   //
161349cc55cSDimitry Andric   // If an authenticated load results in an exception, the instruction at the
162349cc55cSDimitry Andric   // current PC should be one of LDRAx.
163349cc55cSDimitry Andric   if (bad_address != current_pc && fixed_bad_address != current_pc) {
164349cc55cSDimitry Andric     auto ptrauth_info =
165349cc55cSDimitry Andric         GetPtrauthInstructionInfo(target, arch, current_address);
166349cc55cSDimitry Andric     if (ptrauth_info && ptrauth_info->IsAuthenticated && ptrauth_info->IsLoad) {
167349cc55cSDimitry Andric       emit_ptrauth_prologue(bad_address);
168349cc55cSDimitry Andric       strm.Printf("Found authenticated load instruction ");
169349cc55cSDimitry Andric       DescribeAddressBriefly(strm, current_address, target);
170349cc55cSDimitry Andric       m_description = std::string(strm.GetString());
171349cc55cSDimitry Andric       return true;
172349cc55cSDimitry Andric     }
173349cc55cSDimitry Andric   }
174349cc55cSDimitry Andric 
175349cc55cSDimitry Andric   // Detect: BLRAA, BLRAAZ, BLRAB, BLRABZ (Branch with Link to Register, with
176349cc55cSDimitry Andric   // pointer authentication).
177349cc55cSDimitry Andric   //
178349cc55cSDimitry Andric   // TODO: Detect: BRAA, BRAAZ, BRAB, BRABZ (Branch to Register, with pointer
179349cc55cSDimitry Andric   // authentication). At a minimum, this requires call site info support for
180349cc55cSDimitry Andric   // indirect calls.
181349cc55cSDimitry Andric   //
182349cc55cSDimitry Andric   // If an authenticated call or tail call results in an exception, stripping
183349cc55cSDimitry Andric   // the bad address should give the current PC, which points to the address
184349cc55cSDimitry Andric   // we tried to branch to.
185349cc55cSDimitry Andric   if (bad_address != current_pc && fixed_bad_address == current_pc) {
186349cc55cSDimitry Andric     if (StackFrameSP parent_frame = thread.GetStackFrameAtIndex(1)) {
187349cc55cSDimitry Andric       addr_t return_pc =
188349cc55cSDimitry Andric           parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
189349cc55cSDimitry Andric       Address blr_address;
190349cc55cSDimitry Andric       if (!target.ResolveLoadAddress(return_pc - 4, blr_address))
191349cc55cSDimitry Andric         return false;
192349cc55cSDimitry Andric 
193349cc55cSDimitry Andric       auto blr_ptrauth_info =
194349cc55cSDimitry Andric           GetPtrauthInstructionInfo(target, arch, blr_address);
195349cc55cSDimitry Andric       if (blr_ptrauth_info && blr_ptrauth_info->IsAuthenticated &&
196349cc55cSDimitry Andric           blr_ptrauth_info->DoesBranch) {
197349cc55cSDimitry Andric         emit_ptrauth_prologue(bad_address);
198349cc55cSDimitry Andric         strm.Printf("Found authenticated indirect branch ");
199349cc55cSDimitry Andric         DescribeAddressBriefly(strm, blr_address, target);
200349cc55cSDimitry Andric         m_description = std::string(strm.GetString());
201349cc55cSDimitry Andric         return true;
202349cc55cSDimitry Andric       }
203349cc55cSDimitry Andric     }
204349cc55cSDimitry Andric   }
205349cc55cSDimitry Andric 
206349cc55cSDimitry Andric   // TODO: Detect: RETAA, RETAB (Return from subroutine, with pointer
207349cc55cSDimitry Andric   // authentication).
208349cc55cSDimitry Andric   //
209349cc55cSDimitry Andric   // Is there a motivating, non-malicious code snippet that corrupts LR?
210349cc55cSDimitry Andric 
211349cc55cSDimitry Andric   return false;
212349cc55cSDimitry Andric }
213349cc55cSDimitry Andric 
2140b57cec5SDimitry Andric const char *StopInfoMachException::GetDescription() {
2159dba64beSDimitry Andric   if (!m_description.empty())
2169dba64beSDimitry Andric     return m_description.c_str();
2179dba64beSDimitry Andric   if (GetValue() == eStopReasonInvalid)
2189dba64beSDimitry Andric     return "invalid stop reason!";
2199dba64beSDimitry Andric 
2200b57cec5SDimitry Andric   ExecutionContext exe_ctx(m_thread_wp.lock());
2210b57cec5SDimitry Andric   Target *target = exe_ctx.GetTargetPtr();
2220b57cec5SDimitry Andric   const llvm::Triple::ArchType cpu =
2230b57cec5SDimitry Andric       target ? target->GetArchitecture().GetMachine()
2240b57cec5SDimitry Andric              : llvm::Triple::UnknownArch;
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric   const char *exc_desc = nullptr;
2270b57cec5SDimitry Andric   const char *code_label = "code";
2280b57cec5SDimitry Andric   const char *code_desc = nullptr;
2290b57cec5SDimitry Andric   const char *subcode_label = "subcode";
2300b57cec5SDimitry Andric   const char *subcode_desc = nullptr;
2310b57cec5SDimitry Andric 
2320b57cec5SDimitry Andric #if defined(__APPLE__)
2330b57cec5SDimitry Andric   char code_desc_buf[32];
2340b57cec5SDimitry Andric   char subcode_desc_buf[32];
2350b57cec5SDimitry Andric #endif
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric   switch (m_value) {
2380b57cec5SDimitry Andric   case 1: // EXC_BAD_ACCESS
2390b57cec5SDimitry Andric     exc_desc = "EXC_BAD_ACCESS";
2400b57cec5SDimitry Andric     subcode_label = "address";
2410b57cec5SDimitry Andric     switch (cpu) {
2420b57cec5SDimitry Andric     case llvm::Triple::x86:
2430b57cec5SDimitry Andric     case llvm::Triple::x86_64:
2440b57cec5SDimitry Andric       switch (m_exc_code) {
2450b57cec5SDimitry Andric       case 0xd:
2460b57cec5SDimitry Andric         code_desc = "EXC_I386_GPFLT";
2470b57cec5SDimitry Andric         m_exc_data_count = 1;
2480b57cec5SDimitry Andric         break;
2490b57cec5SDimitry Andric       }
2500b57cec5SDimitry Andric       break;
2510b57cec5SDimitry Andric     case llvm::Triple::arm:
2520b57cec5SDimitry Andric     case llvm::Triple::thumb:
2530b57cec5SDimitry Andric       switch (m_exc_code) {
2540b57cec5SDimitry Andric       case 0x101:
2550b57cec5SDimitry Andric         code_desc = "EXC_ARM_DA_ALIGN";
2560b57cec5SDimitry Andric         break;
2570b57cec5SDimitry Andric       case 0x102:
2580b57cec5SDimitry Andric         code_desc = "EXC_ARM_DA_DEBUG";
2590b57cec5SDimitry Andric         break;
2600b57cec5SDimitry Andric       }
2610b57cec5SDimitry Andric       break;
2620b57cec5SDimitry Andric 
263349cc55cSDimitry Andric     case llvm::Triple::aarch64:
264349cc55cSDimitry Andric       if (DeterminePtrauthFailure(exe_ctx))
265349cc55cSDimitry Andric         return m_description.c_str();
266349cc55cSDimitry Andric       break;
267349cc55cSDimitry Andric 
2680b57cec5SDimitry Andric     default:
2690b57cec5SDimitry Andric       break;
2700b57cec5SDimitry Andric     }
2710b57cec5SDimitry Andric     break;
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric   case 2: // EXC_BAD_INSTRUCTION
2740b57cec5SDimitry Andric     exc_desc = "EXC_BAD_INSTRUCTION";
2750b57cec5SDimitry Andric     switch (cpu) {
2760b57cec5SDimitry Andric     case llvm::Triple::x86:
2770b57cec5SDimitry Andric     case llvm::Triple::x86_64:
2780b57cec5SDimitry Andric       if (m_exc_code == 1)
2790b57cec5SDimitry Andric         code_desc = "EXC_I386_INVOP";
2800b57cec5SDimitry Andric       break;
2810b57cec5SDimitry Andric 
2820b57cec5SDimitry Andric     case llvm::Triple::arm:
2830b57cec5SDimitry Andric     case llvm::Triple::thumb:
2840b57cec5SDimitry Andric       if (m_exc_code == 1)
2850b57cec5SDimitry Andric         code_desc = "EXC_ARM_UNDEFINED";
2860b57cec5SDimitry Andric       break;
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric     default:
2890b57cec5SDimitry Andric       break;
2900b57cec5SDimitry Andric     }
2910b57cec5SDimitry Andric     break;
2920b57cec5SDimitry Andric 
2930b57cec5SDimitry Andric   case 3: // EXC_ARITHMETIC
2940b57cec5SDimitry Andric     exc_desc = "EXC_ARITHMETIC";
2950b57cec5SDimitry Andric     switch (cpu) {
2960b57cec5SDimitry Andric     case llvm::Triple::x86:
2970b57cec5SDimitry Andric     case llvm::Triple::x86_64:
2980b57cec5SDimitry Andric       switch (m_exc_code) {
2990b57cec5SDimitry Andric       case 1:
3000b57cec5SDimitry Andric         code_desc = "EXC_I386_DIV";
3010b57cec5SDimitry Andric         break;
3020b57cec5SDimitry Andric       case 2:
3030b57cec5SDimitry Andric         code_desc = "EXC_I386_INTO";
3040b57cec5SDimitry Andric         break;
3050b57cec5SDimitry Andric       case 3:
3060b57cec5SDimitry Andric         code_desc = "EXC_I386_NOEXT";
3070b57cec5SDimitry Andric         break;
3080b57cec5SDimitry Andric       case 4:
3090b57cec5SDimitry Andric         code_desc = "EXC_I386_EXTOVR";
3100b57cec5SDimitry Andric         break;
3110b57cec5SDimitry Andric       case 5:
3120b57cec5SDimitry Andric         code_desc = "EXC_I386_EXTERR";
3130b57cec5SDimitry Andric         break;
3140b57cec5SDimitry Andric       case 6:
3150b57cec5SDimitry Andric         code_desc = "EXC_I386_EMERR";
3160b57cec5SDimitry Andric         break;
3170b57cec5SDimitry Andric       case 7:
3180b57cec5SDimitry Andric         code_desc = "EXC_I386_BOUND";
3190b57cec5SDimitry Andric         break;
3200b57cec5SDimitry Andric       case 8:
3210b57cec5SDimitry Andric         code_desc = "EXC_I386_SSEEXTERR";
3220b57cec5SDimitry Andric         break;
3230b57cec5SDimitry Andric       }
3240b57cec5SDimitry Andric       break;
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric     default:
3270b57cec5SDimitry Andric       break;
3280b57cec5SDimitry Andric     }
3290b57cec5SDimitry Andric     break;
3300b57cec5SDimitry Andric 
3310b57cec5SDimitry Andric   case 4: // EXC_EMULATION
3320b57cec5SDimitry Andric     exc_desc = "EXC_EMULATION";
3330b57cec5SDimitry Andric     break;
3340b57cec5SDimitry Andric 
3350b57cec5SDimitry Andric   case 5: // EXC_SOFTWARE
3360b57cec5SDimitry Andric     exc_desc = "EXC_SOFTWARE";
3370b57cec5SDimitry Andric     if (m_exc_code == 0x10003) {
3380b57cec5SDimitry Andric       subcode_desc = "EXC_SOFT_SIGNAL";
3390b57cec5SDimitry Andric       subcode_label = "signo";
3400b57cec5SDimitry Andric     }
3410b57cec5SDimitry Andric     break;
3420b57cec5SDimitry Andric 
3430b57cec5SDimitry Andric   case 6: // EXC_BREAKPOINT
3440b57cec5SDimitry Andric   {
3450b57cec5SDimitry Andric     exc_desc = "EXC_BREAKPOINT";
3460b57cec5SDimitry Andric     switch (cpu) {
3470b57cec5SDimitry Andric     case llvm::Triple::x86:
3480b57cec5SDimitry Andric     case llvm::Triple::x86_64:
3490b57cec5SDimitry Andric       switch (m_exc_code) {
3500b57cec5SDimitry Andric       case 1:
3510b57cec5SDimitry Andric         code_desc = "EXC_I386_SGL";
3520b57cec5SDimitry Andric         break;
3530b57cec5SDimitry Andric       case 2:
3540b57cec5SDimitry Andric         code_desc = "EXC_I386_BPT";
3550b57cec5SDimitry Andric         break;
3560b57cec5SDimitry Andric       }
3570b57cec5SDimitry Andric       break;
3580b57cec5SDimitry Andric 
3590b57cec5SDimitry Andric     case llvm::Triple::arm:
3600b57cec5SDimitry Andric     case llvm::Triple::thumb:
3610b57cec5SDimitry Andric       switch (m_exc_code) {
3620b57cec5SDimitry Andric       case 0x101:
3630b57cec5SDimitry Andric         code_desc = "EXC_ARM_DA_ALIGN";
3640b57cec5SDimitry Andric         break;
3650b57cec5SDimitry Andric       case 0x102:
3660b57cec5SDimitry Andric         code_desc = "EXC_ARM_DA_DEBUG";
3670b57cec5SDimitry Andric         break;
3680b57cec5SDimitry Andric       case 1:
3690b57cec5SDimitry Andric         code_desc = "EXC_ARM_BREAKPOINT";
3700b57cec5SDimitry Andric         break;
3710b57cec5SDimitry Andric       // FIXME temporary workaround, exc_code 0 does not really mean
3720b57cec5SDimitry Andric       // EXC_ARM_BREAKPOINT
3730b57cec5SDimitry Andric       case 0:
3740b57cec5SDimitry Andric         code_desc = "EXC_ARM_BREAKPOINT";
3750b57cec5SDimitry Andric         break;
3760b57cec5SDimitry Andric       }
3770b57cec5SDimitry Andric       break;
3780b57cec5SDimitry Andric 
379349cc55cSDimitry Andric     case llvm::Triple::aarch64:
380349cc55cSDimitry Andric       if (DeterminePtrauthFailure(exe_ctx))
381349cc55cSDimitry Andric         return m_description.c_str();
382349cc55cSDimitry Andric       break;
383349cc55cSDimitry Andric 
3840b57cec5SDimitry Andric     default:
3850b57cec5SDimitry Andric       break;
3860b57cec5SDimitry Andric     }
3870b57cec5SDimitry Andric   } break;
3880b57cec5SDimitry Andric 
3890b57cec5SDimitry Andric   case 7:
3900b57cec5SDimitry Andric     exc_desc = "EXC_SYSCALL";
3910b57cec5SDimitry Andric     break;
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric   case 8:
3940b57cec5SDimitry Andric     exc_desc = "EXC_MACH_SYSCALL";
3950b57cec5SDimitry Andric     break;
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   case 9:
3980b57cec5SDimitry Andric     exc_desc = "EXC_RPC_ALERT";
3990b57cec5SDimitry Andric     break;
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric   case 10:
4020b57cec5SDimitry Andric     exc_desc = "EXC_CRASH";
4030b57cec5SDimitry Andric     break;
4040b57cec5SDimitry Andric   case 11:
4050b57cec5SDimitry Andric     exc_desc = "EXC_RESOURCE";
4060b57cec5SDimitry Andric #if defined(__APPLE__)
4070b57cec5SDimitry Andric     {
4080b57cec5SDimitry Andric       int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code);
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric       code_label = "limit";
4110b57cec5SDimitry Andric       code_desc = code_desc_buf;
4120b57cec5SDimitry Andric       subcode_label = "observed";
4130b57cec5SDimitry Andric       subcode_desc = subcode_desc_buf;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric       switch (resource_type) {
4160b57cec5SDimitry Andric       case RESOURCE_TYPE_CPU:
417bdd1243dSDimitry Andric         exc_desc =
418bdd1243dSDimitry Andric             "EXC_RESOURCE (RESOURCE_TYPE_CPU: CPU usage monitor tripped)";
4190b57cec5SDimitry Andric         snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%",
4200b57cec5SDimitry Andric                  (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code));
4210b57cec5SDimitry Andric         snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%",
4229dba64beSDimitry Andric                  (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED(
4239dba64beSDimitry Andric                      m_exc_subcode));
4240b57cec5SDimitry Andric         break;
4250b57cec5SDimitry Andric       case RESOURCE_TYPE_WAKEUPS:
426bdd1243dSDimitry Andric         exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_WAKEUPS: idle wakeups monitor "
427bdd1243dSDimitry Andric                    "tripped)";
4289dba64beSDimitry Andric         snprintf(
4299dba64beSDimitry Andric             code_desc_buf, sizeof(code_desc_buf), "%d w/s",
4300b57cec5SDimitry Andric             (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code));
4310b57cec5SDimitry Andric         snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s",
4329dba64beSDimitry Andric                  (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED(
4339dba64beSDimitry Andric                      m_exc_subcode));
4340b57cec5SDimitry Andric         break;
4350b57cec5SDimitry Andric       case RESOURCE_TYPE_MEMORY:
436bdd1243dSDimitry Andric         exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory "
437bdd1243dSDimitry Andric                    "limit exceeded)";
4380b57cec5SDimitry Andric         snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
4390b57cec5SDimitry Andric                  (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code));
4400b57cec5SDimitry Andric         subcode_desc = nullptr;
441bdd1243dSDimitry Andric         subcode_label = nullptr;
4420b57cec5SDimitry Andric         break;
4439dba64beSDimitry Andric #if defined(RESOURCE_TYPE_IO)
4449dba64beSDimitry Andric       // RESOURCE_TYPE_IO is introduced in macOS SDK 10.12.
4450b57cec5SDimitry Andric       case RESOURCE_TYPE_IO:
4460b57cec5SDimitry Andric         exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO";
4470b57cec5SDimitry Andric         snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
4480b57cec5SDimitry Andric                  (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code));
4490b57cec5SDimitry Andric         snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB",
4509dba64beSDimitry Andric                  (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode));
4519dba64beSDimitry Andric         ;
4520b57cec5SDimitry Andric         break;
4539dba64beSDimitry Andric #endif
4540b57cec5SDimitry Andric       }
4550b57cec5SDimitry Andric     }
4560b57cec5SDimitry Andric #endif
4570b57cec5SDimitry Andric     break;
4580b57cec5SDimitry Andric   case 12:
4590b57cec5SDimitry Andric     exc_desc = "EXC_GUARD";
4600b57cec5SDimitry Andric     break;
4610b57cec5SDimitry Andric   }
4620b57cec5SDimitry Andric 
4630b57cec5SDimitry Andric   StreamString strm;
4640b57cec5SDimitry Andric 
4650b57cec5SDimitry Andric   if (exc_desc)
4660b57cec5SDimitry Andric     strm.PutCString(exc_desc);
4670b57cec5SDimitry Andric   else
4680b57cec5SDimitry Andric     strm.Printf("EXC_??? (%" PRIu64 ")", m_value);
4690b57cec5SDimitry Andric 
4700b57cec5SDimitry Andric   if (m_exc_data_count >= 1) {
4710b57cec5SDimitry Andric     if (code_desc)
4720b57cec5SDimitry Andric       strm.Printf(" (%s=%s", code_label, code_desc);
4730b57cec5SDimitry Andric     else
4740b57cec5SDimitry Andric       strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code);
4750b57cec5SDimitry Andric   }
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   if (m_exc_data_count >= 2) {
478bdd1243dSDimitry Andric     if (subcode_label && subcode_desc)
4790b57cec5SDimitry Andric       strm.Printf(", %s=%s", subcode_label, subcode_desc);
480bdd1243dSDimitry Andric     else if (subcode_label)
4810b57cec5SDimitry Andric       strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
4820b57cec5SDimitry Andric   }
4830b57cec5SDimitry Andric 
4840b57cec5SDimitry Andric   if (m_exc_data_count > 0)
4850b57cec5SDimitry Andric     strm.PutChar(')');
4860b57cec5SDimitry Andric 
4875ffd83dbSDimitry Andric   m_description = std::string(strm.GetString());
4880b57cec5SDimitry Andric   return m_description.c_str();
4890b57cec5SDimitry Andric }
4900b57cec5SDimitry Andric 
4915ffd83dbSDimitry Andric static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target,
4925ffd83dbSDimitry Andric                                            uint32_t exc_data_count,
4935ffd83dbSDimitry Andric                                            uint64_t exc_sub_code,
4945ffd83dbSDimitry Andric                                            uint64_t exc_sub_sub_code) {
4955ffd83dbSDimitry Andric   // Try hardware watchpoint.
4965ffd83dbSDimitry Andric   if (target) {
4975ffd83dbSDimitry Andric     // The exc_sub_code indicates the data break address.
498*0fca6ea1SDimitry Andric     WatchpointResourceSP wp_rsrc_sp =
499*0fca6ea1SDimitry Andric         target->GetProcessSP()->GetWatchpointResourceList().FindByAddress(
500*0fca6ea1SDimitry Andric             (addr_t)exc_sub_code);
501*0fca6ea1SDimitry Andric     if (wp_rsrc_sp && wp_rsrc_sp->GetNumberOfConstituents() > 0) {
502*0fca6ea1SDimitry Andric       return StopInfo::CreateStopReasonWithWatchpointID(
503*0fca6ea1SDimitry Andric           thread, wp_rsrc_sp->GetConstituentAtIndex(0)->GetID());
5045ffd83dbSDimitry Andric     }
5055ffd83dbSDimitry Andric   }
5065ffd83dbSDimitry Andric 
5075ffd83dbSDimitry Andric   // Try hardware breakpoint.
5085ffd83dbSDimitry Andric   ProcessSP process_sp(thread.GetProcess());
5095ffd83dbSDimitry Andric   if (process_sp) {
5105ffd83dbSDimitry Andric     // The exc_sub_code indicates the data break address.
5115ffd83dbSDimitry Andric     lldb::BreakpointSiteSP bp_sp =
5125ffd83dbSDimitry Andric         process_sp->GetBreakpointSiteList().FindByAddress(
5135ffd83dbSDimitry Andric             (lldb::addr_t)exc_sub_code);
5145ffd83dbSDimitry Andric     if (bp_sp && bp_sp->IsEnabled()) {
5155ffd83dbSDimitry Andric       return StopInfo::CreateStopReasonWithBreakpointSiteID(thread,
5165ffd83dbSDimitry Andric                                                             bp_sp->GetID());
5175ffd83dbSDimitry Andric     }
5185ffd83dbSDimitry Andric   }
5195ffd83dbSDimitry Andric 
5205ffd83dbSDimitry Andric   return nullptr;
5215ffd83dbSDimitry Andric }
5225ffd83dbSDimitry Andric 
523bdd1243dSDimitry Andric #if defined(__APPLE__)
524bdd1243dSDimitry Andric const char *
525bdd1243dSDimitry Andric StopInfoMachException::MachException::Name(exception_type_t exc_type) {
526bdd1243dSDimitry Andric   switch (exc_type) {
527bdd1243dSDimitry Andric   case EXC_BAD_ACCESS:
528bdd1243dSDimitry Andric     return "EXC_BAD_ACCESS";
529bdd1243dSDimitry Andric   case EXC_BAD_INSTRUCTION:
530bdd1243dSDimitry Andric     return "EXC_BAD_INSTRUCTION";
531bdd1243dSDimitry Andric   case EXC_ARITHMETIC:
532bdd1243dSDimitry Andric     return "EXC_ARITHMETIC";
533bdd1243dSDimitry Andric   case EXC_EMULATION:
534bdd1243dSDimitry Andric     return "EXC_EMULATION";
535bdd1243dSDimitry Andric   case EXC_SOFTWARE:
536bdd1243dSDimitry Andric     return "EXC_SOFTWARE";
537bdd1243dSDimitry Andric   case EXC_BREAKPOINT:
538bdd1243dSDimitry Andric     return "EXC_BREAKPOINT";
539bdd1243dSDimitry Andric   case EXC_SYSCALL:
540bdd1243dSDimitry Andric     return "EXC_SYSCALL";
541bdd1243dSDimitry Andric   case EXC_MACH_SYSCALL:
542bdd1243dSDimitry Andric     return "EXC_MACH_SYSCALL";
543bdd1243dSDimitry Andric   case EXC_RPC_ALERT:
544bdd1243dSDimitry Andric     return "EXC_RPC_ALERT";
545bdd1243dSDimitry Andric #ifdef EXC_CRASH
546bdd1243dSDimitry Andric   case EXC_CRASH:
547bdd1243dSDimitry Andric     return "EXC_CRASH";
548bdd1243dSDimitry Andric #endif
549bdd1243dSDimitry Andric   case EXC_RESOURCE:
550bdd1243dSDimitry Andric     return "EXC_RESOURCE";
551bdd1243dSDimitry Andric #ifdef EXC_GUARD
552bdd1243dSDimitry Andric   case EXC_GUARD:
553bdd1243dSDimitry Andric     return "EXC_GUARD";
554bdd1243dSDimitry Andric #endif
555bdd1243dSDimitry Andric #ifdef EXC_CORPSE_NOTIFY
556bdd1243dSDimitry Andric   case EXC_CORPSE_NOTIFY:
557bdd1243dSDimitry Andric     return "EXC_CORPSE_NOTIFY";
558bdd1243dSDimitry Andric #endif
559bdd1243dSDimitry Andric #ifdef EXC_CORPSE_VARIANT_BIT
560bdd1243dSDimitry Andric   case EXC_CORPSE_VARIANT_BIT:
561bdd1243dSDimitry Andric     return "EXC_CORPSE_VARIANT_BIT";
562bdd1243dSDimitry Andric #endif
563bdd1243dSDimitry Andric   default:
564bdd1243dSDimitry Andric     break;
565bdd1243dSDimitry Andric   }
566bdd1243dSDimitry Andric   return NULL;
567bdd1243dSDimitry Andric }
568bdd1243dSDimitry Andric 
569bdd1243dSDimitry Andric std::optional<exception_type_t>
570bdd1243dSDimitry Andric StopInfoMachException::MachException::ExceptionCode(const char *name) {
571bdd1243dSDimitry Andric   return llvm::StringSwitch<std::optional<exception_type_t>>(name)
572bdd1243dSDimitry Andric       .Case("EXC_BAD_ACCESS", EXC_BAD_ACCESS)
573bdd1243dSDimitry Andric       .Case("EXC_BAD_INSTRUCTION", EXC_BAD_INSTRUCTION)
574bdd1243dSDimitry Andric       .Case("EXC_ARITHMETIC", EXC_ARITHMETIC)
575bdd1243dSDimitry Andric       .Case("EXC_EMULATION", EXC_EMULATION)
576bdd1243dSDimitry Andric       .Case("EXC_SOFTWARE", EXC_SOFTWARE)
577bdd1243dSDimitry Andric       .Case("EXC_BREAKPOINT", EXC_BREAKPOINT)
578bdd1243dSDimitry Andric       .Case("EXC_SYSCALL", EXC_SYSCALL)
579bdd1243dSDimitry Andric       .Case("EXC_MACH_SYSCALL", EXC_MACH_SYSCALL)
580bdd1243dSDimitry Andric       .Case("EXC_RPC_ALERT", EXC_RPC_ALERT)
581bdd1243dSDimitry Andric #ifdef EXC_CRASH
582bdd1243dSDimitry Andric       .Case("EXC_CRASH", EXC_CRASH)
583bdd1243dSDimitry Andric #endif
584bdd1243dSDimitry Andric       .Case("EXC_RESOURCE", EXC_RESOURCE)
585bdd1243dSDimitry Andric #ifdef EXC_GUARD
586bdd1243dSDimitry Andric       .Case("EXC_GUARD", EXC_GUARD)
587bdd1243dSDimitry Andric #endif
588bdd1243dSDimitry Andric #ifdef EXC_CORPSE_NOTIFY
589bdd1243dSDimitry Andric       .Case("EXC_CORPSE_NOTIFY", EXC_CORPSE_NOTIFY)
590bdd1243dSDimitry Andric #endif
591bdd1243dSDimitry Andric       .Default(std::nullopt);
592bdd1243dSDimitry Andric }
593bdd1243dSDimitry Andric #endif
594bdd1243dSDimitry Andric 
5950b57cec5SDimitry Andric StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
5960b57cec5SDimitry Andric     Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
5970b57cec5SDimitry Andric     uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
5980b57cec5SDimitry Andric     bool pc_already_adjusted, bool adjust_pc_if_needed) {
5999dba64beSDimitry Andric   if (exc_type == 0)
6009dba64beSDimitry Andric     return StopInfoSP();
6019dba64beSDimitry Andric 
602*0fca6ea1SDimitry Andric   bool not_stepping_but_got_singlestep_exception = false;
6030b57cec5SDimitry Andric   uint32_t pc_decrement = 0;
6040b57cec5SDimitry Andric   ExecutionContext exe_ctx(thread.shared_from_this());
6050b57cec5SDimitry Andric   Target *target = exe_ctx.GetTargetPtr();
6060b57cec5SDimitry Andric   const llvm::Triple::ArchType cpu =
6070b57cec5SDimitry Andric       target ? target->GetArchitecture().GetMachine()
6080b57cec5SDimitry Andric              : llvm::Triple::UnknownArch;
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric   switch (exc_type) {
6110b57cec5SDimitry Andric   case 1: // EXC_BAD_ACCESS
6120b57cec5SDimitry Andric   case 2: // EXC_BAD_INSTRUCTION
6130b57cec5SDimitry Andric   case 3: // EXC_ARITHMETIC
6140b57cec5SDimitry Andric   case 4: // EXC_EMULATION
6150b57cec5SDimitry Andric     break;
6160b57cec5SDimitry Andric 
6170b57cec5SDimitry Andric   case 5:                    // EXC_SOFTWARE
6180b57cec5SDimitry Andric     if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
6190b57cec5SDimitry Andric     {
6200b57cec5SDimitry Andric       if (exc_sub_code == 5) {
6210b57cec5SDimitry Andric         // On MacOSX, a SIGTRAP can signify that a process has called exec,
6220b57cec5SDimitry Andric         // so we should check with our dynamic loader to verify.
6230b57cec5SDimitry Andric         ProcessSP process_sp(thread.GetProcess());
6240b57cec5SDimitry Andric         if (process_sp) {
6250b57cec5SDimitry Andric           DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
6260b57cec5SDimitry Andric           if (dynamic_loader && dynamic_loader->ProcessDidExec()) {
6270b57cec5SDimitry Andric             // The program was re-exec'ed
6280b57cec5SDimitry Andric             return StopInfo::CreateStopReasonWithExec(thread);
6290b57cec5SDimitry Andric           }
6300b57cec5SDimitry Andric         }
6310b57cec5SDimitry Andric       }
6320b57cec5SDimitry Andric       return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code);
6330b57cec5SDimitry Andric     }
6340b57cec5SDimitry Andric     break;
6350b57cec5SDimitry Andric 
6360b57cec5SDimitry Andric   case 6: // EXC_BREAKPOINT
6370b57cec5SDimitry Andric   {
6380b57cec5SDimitry Andric     bool is_actual_breakpoint = false;
6390b57cec5SDimitry Andric     bool is_trace_if_actual_breakpoint_missing = false;
6400b57cec5SDimitry Andric     switch (cpu) {
6410b57cec5SDimitry Andric     case llvm::Triple::x86:
6420b57cec5SDimitry Andric     case llvm::Triple::x86_64:
6430b57cec5SDimitry Andric       if (exc_code == 1) // EXC_I386_SGL
6440b57cec5SDimitry Andric       {
6450b57cec5SDimitry Andric         if (!exc_sub_code) {
6460b57cec5SDimitry Andric           // This looks like a plain trap.
6470b57cec5SDimitry Andric           // Have to check if there is a breakpoint here as well.  When you
6480b57cec5SDimitry Andric           // single-step onto a trap, the single step stops you not to trap.
6490b57cec5SDimitry Andric           // Since we also do that check below, let's just use that logic.
6500b57cec5SDimitry Andric           is_actual_breakpoint = true;
6510b57cec5SDimitry Andric           is_trace_if_actual_breakpoint_missing = true;
6520b57cec5SDimitry Andric         } else {
6535ffd83dbSDimitry Andric           if (StopInfoSP stop_info =
6545ffd83dbSDimitry Andric                   GetStopInfoForHardwareBP(thread, target, exc_data_count,
6555ffd83dbSDimitry Andric                                            exc_sub_code, exc_sub_sub_code))
6565ffd83dbSDimitry Andric             return stop_info;
6570b57cec5SDimitry Andric         }
6580b57cec5SDimitry Andric       } else if (exc_code == 2 || // EXC_I386_BPT
6590b57cec5SDimitry Andric                  exc_code == 3)   // EXC_I386_BPTFLT
6600b57cec5SDimitry Andric       {
6610b57cec5SDimitry Andric         // KDP returns EXC_I386_BPTFLT for trace breakpoints
6620b57cec5SDimitry Andric         if (exc_code == 3)
6630b57cec5SDimitry Andric           is_trace_if_actual_breakpoint_missing = true;
6640b57cec5SDimitry Andric 
6650b57cec5SDimitry Andric         is_actual_breakpoint = true;
6660b57cec5SDimitry Andric         if (!pc_already_adjusted)
6670b57cec5SDimitry Andric           pc_decrement = 1;
6680b57cec5SDimitry Andric       }
6690b57cec5SDimitry Andric       break;
6700b57cec5SDimitry Andric 
6710b57cec5SDimitry Andric     case llvm::Triple::arm:
6720b57cec5SDimitry Andric     case llvm::Triple::thumb:
6730b57cec5SDimitry Andric       if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
6740b57cec5SDimitry Andric       {
6755f757f3fSDimitry Andric         // LWP_TODO: We need to find the WatchpointResource that matches
6765f757f3fSDimitry Andric         // the address, and evaluate its Watchpoints.
6775f757f3fSDimitry Andric 
6780b57cec5SDimitry Andric         // It's a watchpoint, then, if the exc_sub_code indicates a
6790b57cec5SDimitry Andric         // known/enabled data break address from our watchpoint list.
6800b57cec5SDimitry Andric         lldb::WatchpointSP wp_sp;
6810b57cec5SDimitry Andric         if (target)
6820b57cec5SDimitry Andric           wp_sp = target->GetWatchpointList().FindByAddress(
6830b57cec5SDimitry Andric               (lldb::addr_t)exc_sub_code);
6840b57cec5SDimitry Andric         if (wp_sp && wp_sp->IsEnabled()) {
6850b57cec5SDimitry Andric           return StopInfo::CreateStopReasonWithWatchpointID(thread,
6860b57cec5SDimitry Andric                                                             wp_sp->GetID());
6870b57cec5SDimitry Andric         } else {
6880b57cec5SDimitry Andric           is_actual_breakpoint = true;
6890b57cec5SDimitry Andric           is_trace_if_actual_breakpoint_missing = true;
6900b57cec5SDimitry Andric         }
6910b57cec5SDimitry Andric       } else if (exc_code == 1) // EXC_ARM_BREAKPOINT
6920b57cec5SDimitry Andric       {
6930b57cec5SDimitry Andric         is_actual_breakpoint = true;
6940b57cec5SDimitry Andric         is_trace_if_actual_breakpoint_missing = true;
6950b57cec5SDimitry Andric       } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel
6960b57cec5SDimitry Andric                                 // is currently returning this so accept it
6970b57cec5SDimitry Andric                                 // as indicating a breakpoint until the
6980b57cec5SDimitry Andric                                 // kernel is fixed
6990b57cec5SDimitry Andric       {
7000b57cec5SDimitry Andric         is_actual_breakpoint = true;
7010b57cec5SDimitry Andric         is_trace_if_actual_breakpoint_missing = true;
7020b57cec5SDimitry Andric       }
7030b57cec5SDimitry Andric       break;
7040b57cec5SDimitry Andric 
7059dba64beSDimitry Andric     case llvm::Triple::aarch64_32:
7060b57cec5SDimitry Andric     case llvm::Triple::aarch64: {
70706c3fb27SDimitry Andric       // xnu describes three things with type EXC_BREAKPOINT:
70806c3fb27SDimitry Andric       //
70906c3fb27SDimitry Andric       //   exc_code 0x102 [EXC_ARM_DA_DEBUG], exc_sub_code addr-of-insn
71006c3fb27SDimitry Andric       //      Watchpoint access.  exc_sub_code is the address of the
71106c3fb27SDimitry Andric       //      instruction which trigged the watchpoint trap.
71206c3fb27SDimitry Andric       //      debugserver may add the watchpoint number that was triggered
71306c3fb27SDimitry Andric       //      in exc_sub_sub_code.
71406c3fb27SDimitry Andric       //
71506c3fb27SDimitry Andric       //   exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code 0
71606c3fb27SDimitry Andric       //      Instruction step has completed.
71706c3fb27SDimitry Andric       //
71806c3fb27SDimitry Andric       //   exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code address-of-instruction
71906c3fb27SDimitry Andric       //      Software breakpoint instruction executed.
72006c3fb27SDimitry Andric 
7210b57cec5SDimitry Andric       if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT
7220b57cec5SDimitry Andric       {
7230b57cec5SDimitry Andric         // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0
7240b57cec5SDimitry Andric         // is set
72506c3fb27SDimitry Andric         is_actual_breakpoint = true;
7260b57cec5SDimitry Andric         is_trace_if_actual_breakpoint_missing = true;
727*0fca6ea1SDimitry Andric         if (thread.GetTemporaryResumeState() != eStateStepping)
728*0fca6ea1SDimitry Andric           not_stepping_but_got_singlestep_exception = true;
7290b57cec5SDimitry Andric       }
7300b57cec5SDimitry Andric       if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
7310b57cec5SDimitry Andric       {
7325f757f3fSDimitry Andric         // LWP_TODO: We need to find the WatchpointResource that matches
7335f757f3fSDimitry Andric         // the address, and evaluate its Watchpoints.
7345f757f3fSDimitry Andric 
7350b57cec5SDimitry Andric         // It's a watchpoint, then, if the exc_sub_code indicates a
7360b57cec5SDimitry Andric         // known/enabled data break address from our watchpoint list.
7370b57cec5SDimitry Andric         lldb::WatchpointSP wp_sp;
7380b57cec5SDimitry Andric         if (target)
7390b57cec5SDimitry Andric           wp_sp = target->GetWatchpointList().FindByAddress(
7400b57cec5SDimitry Andric               (lldb::addr_t)exc_sub_code);
7410b57cec5SDimitry Andric         if (wp_sp && wp_sp->IsEnabled()) {
7420b57cec5SDimitry Andric           return StopInfo::CreateStopReasonWithWatchpointID(thread,
7430b57cec5SDimitry Andric                                                             wp_sp->GetID());
7440b57cec5SDimitry Andric         }
7450b57cec5SDimitry Andric         // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as
7460b57cec5SDimitry Andric         // EXC_BAD_ACCESS
7470b57cec5SDimitry Andric         if (thread.GetTemporaryResumeState() == eStateStepping)
7480b57cec5SDimitry Andric           return StopInfo::CreateStopReasonToTrace(thread);
7490b57cec5SDimitry Andric       }
7500b57cec5SDimitry Andric       // It looks like exc_sub_code has the 4 bytes of the instruction that
7510b57cec5SDimitry Andric       // triggered the exception, i.e. our breakpoint opcode
7520b57cec5SDimitry Andric       is_actual_breakpoint = exc_code == 1;
7530b57cec5SDimitry Andric       break;
7540b57cec5SDimitry Andric     }
7550b57cec5SDimitry Andric 
7560b57cec5SDimitry Andric     default:
7570b57cec5SDimitry Andric       break;
7580b57cec5SDimitry Andric     }
7590b57cec5SDimitry Andric 
7600b57cec5SDimitry Andric     if (is_actual_breakpoint) {
7610b57cec5SDimitry Andric       RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
7620b57cec5SDimitry Andric       addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
7630b57cec5SDimitry Andric 
7640b57cec5SDimitry Andric       ProcessSP process_sp(thread.CalculateProcess());
7650b57cec5SDimitry Andric 
7660b57cec5SDimitry Andric       lldb::BreakpointSiteSP bp_site_sp;
7670b57cec5SDimitry Andric       if (process_sp)
7680b57cec5SDimitry Andric         bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
7690b57cec5SDimitry Andric       if (bp_site_sp && bp_site_sp->IsEnabled()) {
7700b57cec5SDimitry Andric         // Update the PC if we were asked to do so, but only do so if we find
7710b57cec5SDimitry Andric         // a breakpoint that we know about cause this could be a trap
7720b57cec5SDimitry Andric         // instruction in the code
7730b57cec5SDimitry Andric         if (pc_decrement > 0 && adjust_pc_if_needed)
7740b57cec5SDimitry Andric           reg_ctx_sp->SetPC(pc);
7750b57cec5SDimitry Andric 
7760b57cec5SDimitry Andric         // If the breakpoint is for this thread, then we'll report the hit,
7770b57cec5SDimitry Andric         // but if it is for another thread, we can just report no reason.  We
7780b57cec5SDimitry Andric         // don't need to worry about stepping over the breakpoint here, that
7790b57cec5SDimitry Andric         // will be taken care of when the thread resumes and notices that
7800b57cec5SDimitry Andric         // there's a breakpoint under the pc. If we have an operating system
7810b57cec5SDimitry Andric         // plug-in, we might have set a thread specific breakpoint using the
7820b57cec5SDimitry Andric         // operating system thread ID, so we can't make any assumptions about
7830b57cec5SDimitry Andric         // the thread ID so we must always report the breakpoint regardless
7840b57cec5SDimitry Andric         // of the thread.
785fe6060f1SDimitry Andric         if (bp_site_sp->ValidForThisThread(thread) ||
7860b57cec5SDimitry Andric             thread.GetProcess()->GetOperatingSystem() != nullptr)
7870b57cec5SDimitry Andric           return StopInfo::CreateStopReasonWithBreakpointSiteID(
7880b57cec5SDimitry Andric               thread, bp_site_sp->GetID());
7890b57cec5SDimitry Andric         else if (is_trace_if_actual_breakpoint_missing)
7900b57cec5SDimitry Andric           return StopInfo::CreateStopReasonToTrace(thread);
7910b57cec5SDimitry Andric         else
7920b57cec5SDimitry Andric           return StopInfoSP();
7930b57cec5SDimitry Andric       }
7940b57cec5SDimitry Andric 
7950b57cec5SDimitry Andric       // Don't call this a trace if we weren't single stepping this thread.
7960b57cec5SDimitry Andric       if (is_trace_if_actual_breakpoint_missing &&
7970b57cec5SDimitry Andric           thread.GetTemporaryResumeState() == eStateStepping) {
7980b57cec5SDimitry Andric         return StopInfo::CreateStopReasonToTrace(thread);
7990b57cec5SDimitry Andric       }
8000b57cec5SDimitry Andric     }
8010b57cec5SDimitry Andric   } break;
8020b57cec5SDimitry Andric 
8030b57cec5SDimitry Andric   case 7:  // EXC_SYSCALL
8040b57cec5SDimitry Andric   case 8:  // EXC_MACH_SYSCALL
8050b57cec5SDimitry Andric   case 9:  // EXC_RPC_ALERT
8060b57cec5SDimitry Andric   case 10: // EXC_CRASH
8070b57cec5SDimitry Andric     break;
8080b57cec5SDimitry Andric   }
8090b57cec5SDimitry Andric 
810*0fca6ea1SDimitry Andric   return std::make_shared<StopInfoMachException>(
811*0fca6ea1SDimitry Andric       thread, exc_type, exc_data_count, exc_code, exc_sub_code,
812*0fca6ea1SDimitry Andric       not_stepping_but_got_singlestep_exception);
813*0fca6ea1SDimitry Andric }
814*0fca6ea1SDimitry Andric 
815*0fca6ea1SDimitry Andric // Detect an unusual situation on Darwin where:
816*0fca6ea1SDimitry Andric //
817*0fca6ea1SDimitry Andric //   0. We did an instruction-step before this.
818*0fca6ea1SDimitry Andric //   1. We have a hardware breakpoint or watchpoint set.
819*0fca6ea1SDimitry Andric //   2. We resumed the process, but not with an instruction-step.
820*0fca6ea1SDimitry Andric //   3. The thread gets an "instruction-step completed" mach exception.
821*0fca6ea1SDimitry Andric //   4. The pc has not advanced - it is the same as before.
822*0fca6ea1SDimitry Andric //
823*0fca6ea1SDimitry Andric // This method returns true for that combination of events.
824*0fca6ea1SDimitry Andric bool StopInfoMachException::WasContinueInterrupted(Thread &thread) {
825*0fca6ea1SDimitry Andric   Log *log = GetLog(LLDBLog::Step);
826*0fca6ea1SDimitry Andric 
827*0fca6ea1SDimitry Andric   // We got an instruction-step completed mach exception but we were not
828*0fca6ea1SDimitry Andric   // doing an instruction step on this thread.
829*0fca6ea1SDimitry Andric   if (!m_not_stepping_but_got_singlestep_exception)
830*0fca6ea1SDimitry Andric     return false;
831*0fca6ea1SDimitry Andric 
832*0fca6ea1SDimitry Andric   RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
833*0fca6ea1SDimitry Andric   std::optional<addr_t> prev_pc = thread.GetPreviousFrameZeroPC();
834*0fca6ea1SDimitry Andric   if (!reg_ctx_sp || !prev_pc)
835*0fca6ea1SDimitry Andric     return false;
836*0fca6ea1SDimitry Andric 
837*0fca6ea1SDimitry Andric   // The previous pc value and current pc value are the same.
838*0fca6ea1SDimitry Andric   if (*prev_pc != reg_ctx_sp->GetPC())
839*0fca6ea1SDimitry Andric     return false;
840*0fca6ea1SDimitry Andric 
841*0fca6ea1SDimitry Andric   // We have a watchpoint -- this is the kernel bug.
842*0fca6ea1SDimitry Andric   ProcessSP process_sp = thread.GetProcess();
843*0fca6ea1SDimitry Andric   if (process_sp->GetWatchpointResourceList().GetSize()) {
844*0fca6ea1SDimitry Andric     LLDB_LOGF(log,
845*0fca6ea1SDimitry Andric               "Thread stopped with insn-step completed mach exception but "
846*0fca6ea1SDimitry Andric               "thread was not stepping; there is a hardware watchpoint set.");
847*0fca6ea1SDimitry Andric     return true;
848*0fca6ea1SDimitry Andric   }
849*0fca6ea1SDimitry Andric 
850*0fca6ea1SDimitry Andric   // We have a hardware breakpoint -- this is the kernel bug.
851*0fca6ea1SDimitry Andric   auto &bp_site_list = process_sp->GetBreakpointSiteList();
852*0fca6ea1SDimitry Andric   for (auto &site : bp_site_list.Sites()) {
853*0fca6ea1SDimitry Andric     if (site->IsHardware() && site->IsEnabled()) {
854*0fca6ea1SDimitry Andric       LLDB_LOGF(log,
855*0fca6ea1SDimitry Andric                 "Thread stopped with insn-step completed mach exception but "
856*0fca6ea1SDimitry Andric                 "thread was not stepping; there is a hardware breakpoint set.");
857*0fca6ea1SDimitry Andric       return true;
858*0fca6ea1SDimitry Andric     }
859*0fca6ea1SDimitry Andric   }
860*0fca6ea1SDimitry Andric 
861*0fca6ea1SDimitry Andric   return false;
8620b57cec5SDimitry Andric }
863