1dda28197Spatrick //===-- StopInfoMachException.cpp -----------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "StopInfoMachException.h"
10061da546Spatrick
11dda28197Spatrick #include "lldb/lldb-forward.h"
12061da546Spatrick
13061da546Spatrick #if defined(__APPLE__)
14061da546Spatrick // Needed for the EXC_RESOURCE interpretation macros
15061da546Spatrick #include <kern/exc_resource.h>
16061da546Spatrick #endif
17061da546Spatrick
18061da546Spatrick #include "lldb/Breakpoint/Watchpoint.h"
19061da546Spatrick #include "lldb/Symbol/Symbol.h"
20*f6aab3d8Srobert #include "lldb/Target/ABI.h"
21061da546Spatrick #include "lldb/Target/DynamicLoader.h"
22061da546Spatrick #include "lldb/Target/ExecutionContext.h"
23061da546Spatrick #include "lldb/Target/Process.h"
24061da546Spatrick #include "lldb/Target/RegisterContext.h"
25061da546Spatrick #include "lldb/Target/Target.h"
26061da546Spatrick #include "lldb/Target/Thread.h"
27061da546Spatrick #include "lldb/Target/ThreadPlan.h"
28061da546Spatrick #include "lldb/Target/UnixSignals.h"
29061da546Spatrick #include "lldb/Utility/StreamString.h"
30*f6aab3d8Srobert #include <optional>
31061da546Spatrick
32061da546Spatrick using namespace lldb;
33061da546Spatrick using namespace lldb_private;
34061da546Spatrick
35*f6aab3d8Srobert /// Information about a pointer-authentication related instruction.
36*f6aab3d8Srobert struct PtrauthInstructionInfo {
37*f6aab3d8Srobert bool IsAuthenticated;
38*f6aab3d8Srobert bool IsLoad;
39*f6aab3d8Srobert bool DoesBranch;
40*f6aab3d8Srobert };
41*f6aab3d8Srobert
42*f6aab3d8Srobert /// Get any pointer-authentication related information about the instruction
43*f6aab3d8Srobert /// at address \p at_addr.
44*f6aab3d8Srobert static std::optional<PtrauthInstructionInfo>
GetPtrauthInstructionInfo(Target & target,const ArchSpec & arch,const Address & at_addr)45*f6aab3d8Srobert GetPtrauthInstructionInfo(Target &target, const ArchSpec &arch,
46*f6aab3d8Srobert const Address &at_addr) {
47*f6aab3d8Srobert const char *plugin_name = nullptr;
48*f6aab3d8Srobert const char *flavor = nullptr;
49*f6aab3d8Srobert AddressRange range_bounds(at_addr, 4);
50*f6aab3d8Srobert const bool prefer_file_cache = true;
51*f6aab3d8Srobert DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
52*f6aab3d8Srobert arch, plugin_name, flavor, target, range_bounds, prefer_file_cache);
53*f6aab3d8Srobert if (!disassembler_sp)
54*f6aab3d8Srobert return std::nullopt;
55*f6aab3d8Srobert
56*f6aab3d8Srobert InstructionList &insn_list = disassembler_sp->GetInstructionList();
57*f6aab3d8Srobert InstructionSP insn = insn_list.GetInstructionAtIndex(0);
58*f6aab3d8Srobert if (!insn)
59*f6aab3d8Srobert return std::nullopt;
60*f6aab3d8Srobert
61*f6aab3d8Srobert return PtrauthInstructionInfo{insn->IsAuthenticated(), insn->IsLoad(),
62*f6aab3d8Srobert insn->DoesBranch()};
63*f6aab3d8Srobert }
64*f6aab3d8Srobert
65*f6aab3d8Srobert /// Describe the load address of \p addr using the format filename:line:col.
DescribeAddressBriefly(Stream & strm,const Address & addr,Target & target)66*f6aab3d8Srobert static void DescribeAddressBriefly(Stream &strm, const Address &addr,
67*f6aab3d8Srobert Target &target) {
68*f6aab3d8Srobert strm.Printf("at address=0x%" PRIx64, addr.GetLoadAddress(&target));
69*f6aab3d8Srobert StreamString s;
70*f6aab3d8Srobert if (addr.GetDescription(s, target, eDescriptionLevelBrief))
71*f6aab3d8Srobert strm.Printf(" %s", s.GetString().data());
72*f6aab3d8Srobert strm.Printf(".\n");
73*f6aab3d8Srobert }
74*f6aab3d8Srobert
DeterminePtrauthFailure(ExecutionContext & exe_ctx)75*f6aab3d8Srobert bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) {
76*f6aab3d8Srobert bool IsBreakpoint = m_value == 6; // EXC_BREAKPOINT
77*f6aab3d8Srobert bool IsBadAccess = m_value == 1; // EXC_BAD_ACCESS
78*f6aab3d8Srobert if (!IsBreakpoint && !IsBadAccess)
79*f6aab3d8Srobert return false;
80*f6aab3d8Srobert
81*f6aab3d8Srobert // Check that we have a live process.
82*f6aab3d8Srobert if (!exe_ctx.HasProcessScope() || !exe_ctx.HasThreadScope() ||
83*f6aab3d8Srobert !exe_ctx.HasTargetScope())
84*f6aab3d8Srobert return false;
85*f6aab3d8Srobert
86*f6aab3d8Srobert Thread &thread = *exe_ctx.GetThreadPtr();
87*f6aab3d8Srobert StackFrameSP current_frame = thread.GetStackFrameAtIndex(0);
88*f6aab3d8Srobert if (!current_frame)
89*f6aab3d8Srobert return false;
90*f6aab3d8Srobert
91*f6aab3d8Srobert Target &target = *exe_ctx.GetTargetPtr();
92*f6aab3d8Srobert Process &process = *exe_ctx.GetProcessPtr();
93*f6aab3d8Srobert ABISP abi_sp = process.GetABI();
94*f6aab3d8Srobert const ArchSpec &arch = target.GetArchitecture();
95*f6aab3d8Srobert assert(abi_sp && "Missing ABI info");
96*f6aab3d8Srobert
97*f6aab3d8Srobert // Check for a ptrauth-enabled target.
98*f6aab3d8Srobert const bool ptrauth_enabled_target =
99*f6aab3d8Srobert arch.GetCore() == ArchSpec::eCore_arm_arm64e;
100*f6aab3d8Srobert if (!ptrauth_enabled_target)
101*f6aab3d8Srobert return false;
102*f6aab3d8Srobert
103*f6aab3d8Srobert // Set up a stream we can write a diagnostic into.
104*f6aab3d8Srobert StreamString strm;
105*f6aab3d8Srobert auto emit_ptrauth_prologue = [&](uint64_t at_address) {
106*f6aab3d8Srobert strm.Printf("EXC_BAD_ACCESS (code=%" PRIu64 ", address=0x%" PRIx64 ")\n",
107*f6aab3d8Srobert m_exc_code, at_address);
108*f6aab3d8Srobert strm.Printf("Note: Possible pointer authentication failure detected.\n");
109*f6aab3d8Srobert };
110*f6aab3d8Srobert
111*f6aab3d8Srobert // Check if we have a "brk 0xc47x" trap, where the value that failed to
112*f6aab3d8Srobert // authenticate is in x16.
113*f6aab3d8Srobert Address current_address = current_frame->GetFrameCodeAddress();
114*f6aab3d8Srobert if (IsBreakpoint) {
115*f6aab3d8Srobert RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
116*f6aab3d8Srobert if (!reg_ctx)
117*f6aab3d8Srobert return false;
118*f6aab3d8Srobert
119*f6aab3d8Srobert const RegisterInfo *X16Info = reg_ctx->GetRegisterInfoByName("x16");
120*f6aab3d8Srobert RegisterValue X16Val;
121*f6aab3d8Srobert if (!reg_ctx->ReadRegister(X16Info, X16Val))
122*f6aab3d8Srobert return false;
123*f6aab3d8Srobert uint64_t bad_address = X16Val.GetAsUInt64();
124*f6aab3d8Srobert
125*f6aab3d8Srobert uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
126*f6aab3d8Srobert Address brk_address;
127*f6aab3d8Srobert if (!target.ResolveLoadAddress(fixed_bad_address, brk_address))
128*f6aab3d8Srobert return false;
129*f6aab3d8Srobert
130*f6aab3d8Srobert auto brk_ptrauth_info =
131*f6aab3d8Srobert GetPtrauthInstructionInfo(target, arch, current_address);
132*f6aab3d8Srobert if (brk_ptrauth_info && brk_ptrauth_info->IsAuthenticated) {
133*f6aab3d8Srobert emit_ptrauth_prologue(bad_address);
134*f6aab3d8Srobert strm.Printf("Found value that failed to authenticate ");
135*f6aab3d8Srobert DescribeAddressBriefly(strm, brk_address, target);
136*f6aab3d8Srobert m_description = std::string(strm.GetString());
137*f6aab3d8Srobert return true;
138*f6aab3d8Srobert }
139*f6aab3d8Srobert return false;
140*f6aab3d8Srobert }
141*f6aab3d8Srobert
142*f6aab3d8Srobert assert(IsBadAccess && "Handle EXC_BAD_ACCESS only after this point");
143*f6aab3d8Srobert
144*f6aab3d8Srobert // Check that we have the "bad address" from an EXC_BAD_ACCESS.
145*f6aab3d8Srobert if (m_exc_data_count < 2)
146*f6aab3d8Srobert return false;
147*f6aab3d8Srobert
148*f6aab3d8Srobert // Ok, we know the Target is valid and that it describes a ptrauth-enabled
149*f6aab3d8Srobert // device. Now, we need to determine whether this exception was caused by a
150*f6aab3d8Srobert // ptrauth failure.
151*f6aab3d8Srobert
152*f6aab3d8Srobert uint64_t bad_address = m_exc_subcode;
153*f6aab3d8Srobert uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
154*f6aab3d8Srobert uint64_t current_pc = current_address.GetLoadAddress(&target);
155*f6aab3d8Srobert
156*f6aab3d8Srobert // Detect: LDRAA, LDRAB (Load Register, with pointer authentication).
157*f6aab3d8Srobert //
158*f6aab3d8Srobert // If an authenticated load results in an exception, the instruction at the
159*f6aab3d8Srobert // current PC should be one of LDRAx.
160*f6aab3d8Srobert if (bad_address != current_pc && fixed_bad_address != current_pc) {
161*f6aab3d8Srobert auto ptrauth_info =
162*f6aab3d8Srobert GetPtrauthInstructionInfo(target, arch, current_address);
163*f6aab3d8Srobert if (ptrauth_info && ptrauth_info->IsAuthenticated && ptrauth_info->IsLoad) {
164*f6aab3d8Srobert emit_ptrauth_prologue(bad_address);
165*f6aab3d8Srobert strm.Printf("Found authenticated load instruction ");
166*f6aab3d8Srobert DescribeAddressBriefly(strm, current_address, target);
167*f6aab3d8Srobert m_description = std::string(strm.GetString());
168*f6aab3d8Srobert return true;
169*f6aab3d8Srobert }
170*f6aab3d8Srobert }
171*f6aab3d8Srobert
172*f6aab3d8Srobert // Detect: BLRAA, BLRAAZ, BLRAB, BLRABZ (Branch with Link to Register, with
173*f6aab3d8Srobert // pointer authentication).
174*f6aab3d8Srobert //
175*f6aab3d8Srobert // TODO: Detect: BRAA, BRAAZ, BRAB, BRABZ (Branch to Register, with pointer
176*f6aab3d8Srobert // authentication). At a minimum, this requires call site info support for
177*f6aab3d8Srobert // indirect calls.
178*f6aab3d8Srobert //
179*f6aab3d8Srobert // If an authenticated call or tail call results in an exception, stripping
180*f6aab3d8Srobert // the bad address should give the current PC, which points to the address
181*f6aab3d8Srobert // we tried to branch to.
182*f6aab3d8Srobert if (bad_address != current_pc && fixed_bad_address == current_pc) {
183*f6aab3d8Srobert if (StackFrameSP parent_frame = thread.GetStackFrameAtIndex(1)) {
184*f6aab3d8Srobert addr_t return_pc =
185*f6aab3d8Srobert parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
186*f6aab3d8Srobert Address blr_address;
187*f6aab3d8Srobert if (!target.ResolveLoadAddress(return_pc - 4, blr_address))
188*f6aab3d8Srobert return false;
189*f6aab3d8Srobert
190*f6aab3d8Srobert auto blr_ptrauth_info =
191*f6aab3d8Srobert GetPtrauthInstructionInfo(target, arch, blr_address);
192*f6aab3d8Srobert if (blr_ptrauth_info && blr_ptrauth_info->IsAuthenticated &&
193*f6aab3d8Srobert blr_ptrauth_info->DoesBranch) {
194*f6aab3d8Srobert emit_ptrauth_prologue(bad_address);
195*f6aab3d8Srobert strm.Printf("Found authenticated indirect branch ");
196*f6aab3d8Srobert DescribeAddressBriefly(strm, blr_address, target);
197*f6aab3d8Srobert m_description = std::string(strm.GetString());
198*f6aab3d8Srobert return true;
199*f6aab3d8Srobert }
200*f6aab3d8Srobert }
201*f6aab3d8Srobert }
202*f6aab3d8Srobert
203*f6aab3d8Srobert // TODO: Detect: RETAA, RETAB (Return from subroutine, with pointer
204*f6aab3d8Srobert // authentication).
205*f6aab3d8Srobert //
206*f6aab3d8Srobert // Is there a motivating, non-malicious code snippet that corrupts LR?
207*f6aab3d8Srobert
208*f6aab3d8Srobert return false;
209*f6aab3d8Srobert }
210*f6aab3d8Srobert
GetDescription()211061da546Spatrick const char *StopInfoMachException::GetDescription() {
212061da546Spatrick if (!m_description.empty())
213061da546Spatrick return m_description.c_str();
214061da546Spatrick if (GetValue() == eStopReasonInvalid)
215061da546Spatrick return "invalid stop reason!";
216061da546Spatrick
217061da546Spatrick ExecutionContext exe_ctx(m_thread_wp.lock());
218061da546Spatrick Target *target = exe_ctx.GetTargetPtr();
219061da546Spatrick const llvm::Triple::ArchType cpu =
220061da546Spatrick target ? target->GetArchitecture().GetMachine()
221061da546Spatrick : llvm::Triple::UnknownArch;
222061da546Spatrick
223061da546Spatrick const char *exc_desc = nullptr;
224061da546Spatrick const char *code_label = "code";
225061da546Spatrick const char *code_desc = nullptr;
226061da546Spatrick const char *subcode_label = "subcode";
227061da546Spatrick const char *subcode_desc = nullptr;
228061da546Spatrick
229061da546Spatrick #if defined(__APPLE__)
230061da546Spatrick char code_desc_buf[32];
231061da546Spatrick char subcode_desc_buf[32];
232061da546Spatrick #endif
233061da546Spatrick
234061da546Spatrick switch (m_value) {
235061da546Spatrick case 1: // EXC_BAD_ACCESS
236061da546Spatrick exc_desc = "EXC_BAD_ACCESS";
237061da546Spatrick subcode_label = "address";
238061da546Spatrick switch (cpu) {
239061da546Spatrick case llvm::Triple::x86:
240061da546Spatrick case llvm::Triple::x86_64:
241061da546Spatrick switch (m_exc_code) {
242061da546Spatrick case 0xd:
243061da546Spatrick code_desc = "EXC_I386_GPFLT";
244061da546Spatrick m_exc_data_count = 1;
245061da546Spatrick break;
246061da546Spatrick }
247061da546Spatrick break;
248061da546Spatrick case llvm::Triple::arm:
249061da546Spatrick case llvm::Triple::thumb:
250061da546Spatrick switch (m_exc_code) {
251061da546Spatrick case 0x101:
252061da546Spatrick code_desc = "EXC_ARM_DA_ALIGN";
253061da546Spatrick break;
254061da546Spatrick case 0x102:
255061da546Spatrick code_desc = "EXC_ARM_DA_DEBUG";
256061da546Spatrick break;
257061da546Spatrick }
258061da546Spatrick break;
259061da546Spatrick
260*f6aab3d8Srobert case llvm::Triple::aarch64:
261*f6aab3d8Srobert if (DeterminePtrauthFailure(exe_ctx))
262*f6aab3d8Srobert return m_description.c_str();
263*f6aab3d8Srobert break;
264*f6aab3d8Srobert
265061da546Spatrick default:
266061da546Spatrick break;
267061da546Spatrick }
268061da546Spatrick break;
269061da546Spatrick
270061da546Spatrick case 2: // EXC_BAD_INSTRUCTION
271061da546Spatrick exc_desc = "EXC_BAD_INSTRUCTION";
272061da546Spatrick switch (cpu) {
273061da546Spatrick case llvm::Triple::x86:
274061da546Spatrick case llvm::Triple::x86_64:
275061da546Spatrick if (m_exc_code == 1)
276061da546Spatrick code_desc = "EXC_I386_INVOP";
277061da546Spatrick break;
278061da546Spatrick
279061da546Spatrick case llvm::Triple::arm:
280061da546Spatrick case llvm::Triple::thumb:
281061da546Spatrick if (m_exc_code == 1)
282061da546Spatrick code_desc = "EXC_ARM_UNDEFINED";
283061da546Spatrick break;
284061da546Spatrick
285061da546Spatrick default:
286061da546Spatrick break;
287061da546Spatrick }
288061da546Spatrick break;
289061da546Spatrick
290061da546Spatrick case 3: // EXC_ARITHMETIC
291061da546Spatrick exc_desc = "EXC_ARITHMETIC";
292061da546Spatrick switch (cpu) {
293061da546Spatrick case llvm::Triple::x86:
294061da546Spatrick case llvm::Triple::x86_64:
295061da546Spatrick switch (m_exc_code) {
296061da546Spatrick case 1:
297061da546Spatrick code_desc = "EXC_I386_DIV";
298061da546Spatrick break;
299061da546Spatrick case 2:
300061da546Spatrick code_desc = "EXC_I386_INTO";
301061da546Spatrick break;
302061da546Spatrick case 3:
303061da546Spatrick code_desc = "EXC_I386_NOEXT";
304061da546Spatrick break;
305061da546Spatrick case 4:
306061da546Spatrick code_desc = "EXC_I386_EXTOVR";
307061da546Spatrick break;
308061da546Spatrick case 5:
309061da546Spatrick code_desc = "EXC_I386_EXTERR";
310061da546Spatrick break;
311061da546Spatrick case 6:
312061da546Spatrick code_desc = "EXC_I386_EMERR";
313061da546Spatrick break;
314061da546Spatrick case 7:
315061da546Spatrick code_desc = "EXC_I386_BOUND";
316061da546Spatrick break;
317061da546Spatrick case 8:
318061da546Spatrick code_desc = "EXC_I386_SSEEXTERR";
319061da546Spatrick break;
320061da546Spatrick }
321061da546Spatrick break;
322061da546Spatrick
323061da546Spatrick default:
324061da546Spatrick break;
325061da546Spatrick }
326061da546Spatrick break;
327061da546Spatrick
328061da546Spatrick case 4: // EXC_EMULATION
329061da546Spatrick exc_desc = "EXC_EMULATION";
330061da546Spatrick break;
331061da546Spatrick
332061da546Spatrick case 5: // EXC_SOFTWARE
333061da546Spatrick exc_desc = "EXC_SOFTWARE";
334061da546Spatrick if (m_exc_code == 0x10003) {
335061da546Spatrick subcode_desc = "EXC_SOFT_SIGNAL";
336061da546Spatrick subcode_label = "signo";
337061da546Spatrick }
338061da546Spatrick break;
339061da546Spatrick
340061da546Spatrick case 6: // EXC_BREAKPOINT
341061da546Spatrick {
342061da546Spatrick exc_desc = "EXC_BREAKPOINT";
343061da546Spatrick switch (cpu) {
344061da546Spatrick case llvm::Triple::x86:
345061da546Spatrick case llvm::Triple::x86_64:
346061da546Spatrick switch (m_exc_code) {
347061da546Spatrick case 1:
348061da546Spatrick code_desc = "EXC_I386_SGL";
349061da546Spatrick break;
350061da546Spatrick case 2:
351061da546Spatrick code_desc = "EXC_I386_BPT";
352061da546Spatrick break;
353061da546Spatrick }
354061da546Spatrick break;
355061da546Spatrick
356061da546Spatrick case llvm::Triple::arm:
357061da546Spatrick case llvm::Triple::thumb:
358061da546Spatrick switch (m_exc_code) {
359061da546Spatrick case 0x101:
360061da546Spatrick code_desc = "EXC_ARM_DA_ALIGN";
361061da546Spatrick break;
362061da546Spatrick case 0x102:
363061da546Spatrick code_desc = "EXC_ARM_DA_DEBUG";
364061da546Spatrick break;
365061da546Spatrick case 1:
366061da546Spatrick code_desc = "EXC_ARM_BREAKPOINT";
367061da546Spatrick break;
368061da546Spatrick // FIXME temporary workaround, exc_code 0 does not really mean
369061da546Spatrick // EXC_ARM_BREAKPOINT
370061da546Spatrick case 0:
371061da546Spatrick code_desc = "EXC_ARM_BREAKPOINT";
372061da546Spatrick break;
373061da546Spatrick }
374061da546Spatrick break;
375061da546Spatrick
376*f6aab3d8Srobert case llvm::Triple::aarch64:
377*f6aab3d8Srobert if (DeterminePtrauthFailure(exe_ctx))
378*f6aab3d8Srobert return m_description.c_str();
379*f6aab3d8Srobert break;
380*f6aab3d8Srobert
381061da546Spatrick default:
382061da546Spatrick break;
383061da546Spatrick }
384061da546Spatrick } break;
385061da546Spatrick
386061da546Spatrick case 7:
387061da546Spatrick exc_desc = "EXC_SYSCALL";
388061da546Spatrick break;
389061da546Spatrick
390061da546Spatrick case 8:
391061da546Spatrick exc_desc = "EXC_MACH_SYSCALL";
392061da546Spatrick break;
393061da546Spatrick
394061da546Spatrick case 9:
395061da546Spatrick exc_desc = "EXC_RPC_ALERT";
396061da546Spatrick break;
397061da546Spatrick
398061da546Spatrick case 10:
399061da546Spatrick exc_desc = "EXC_CRASH";
400061da546Spatrick break;
401061da546Spatrick case 11:
402061da546Spatrick exc_desc = "EXC_RESOURCE";
403061da546Spatrick #if defined(__APPLE__)
404061da546Spatrick {
405061da546Spatrick int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code);
406061da546Spatrick
407061da546Spatrick code_label = "limit";
408061da546Spatrick code_desc = code_desc_buf;
409061da546Spatrick subcode_label = "observed";
410061da546Spatrick subcode_desc = subcode_desc_buf;
411061da546Spatrick
412061da546Spatrick switch (resource_type) {
413061da546Spatrick case RESOURCE_TYPE_CPU:
414*f6aab3d8Srobert exc_desc =
415*f6aab3d8Srobert "EXC_RESOURCE (RESOURCE_TYPE_CPU: CPU usage monitor tripped)";
416061da546Spatrick snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%",
417061da546Spatrick (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code));
418061da546Spatrick snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%",
419061da546Spatrick (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED(
420061da546Spatrick m_exc_subcode));
421061da546Spatrick break;
422061da546Spatrick case RESOURCE_TYPE_WAKEUPS:
423*f6aab3d8Srobert exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_WAKEUPS: idle wakeups monitor "
424*f6aab3d8Srobert "tripped)";
425061da546Spatrick snprintf(
426061da546Spatrick code_desc_buf, sizeof(code_desc_buf), "%d w/s",
427061da546Spatrick (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code));
428061da546Spatrick snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s",
429061da546Spatrick (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED(
430061da546Spatrick m_exc_subcode));
431061da546Spatrick break;
432061da546Spatrick case RESOURCE_TYPE_MEMORY:
433*f6aab3d8Srobert exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory "
434*f6aab3d8Srobert "limit exceeded)";
435061da546Spatrick snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
436061da546Spatrick (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code));
437061da546Spatrick subcode_desc = nullptr;
438*f6aab3d8Srobert subcode_label = nullptr;
439061da546Spatrick break;
440061da546Spatrick #if defined(RESOURCE_TYPE_IO)
441061da546Spatrick // RESOURCE_TYPE_IO is introduced in macOS SDK 10.12.
442061da546Spatrick case RESOURCE_TYPE_IO:
443061da546Spatrick exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO";
444061da546Spatrick snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB",
445061da546Spatrick (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code));
446061da546Spatrick snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB",
447061da546Spatrick (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode));
448061da546Spatrick ;
449061da546Spatrick break;
450061da546Spatrick #endif
451061da546Spatrick }
452061da546Spatrick }
453061da546Spatrick #endif
454061da546Spatrick break;
455061da546Spatrick case 12:
456061da546Spatrick exc_desc = "EXC_GUARD";
457061da546Spatrick break;
458061da546Spatrick }
459061da546Spatrick
460061da546Spatrick StreamString strm;
461061da546Spatrick
462061da546Spatrick if (exc_desc)
463061da546Spatrick strm.PutCString(exc_desc);
464061da546Spatrick else
465061da546Spatrick strm.Printf("EXC_??? (%" PRIu64 ")", m_value);
466061da546Spatrick
467061da546Spatrick if (m_exc_data_count >= 1) {
468061da546Spatrick if (code_desc)
469061da546Spatrick strm.Printf(" (%s=%s", code_label, code_desc);
470061da546Spatrick else
471061da546Spatrick strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code);
472061da546Spatrick }
473061da546Spatrick
474061da546Spatrick if (m_exc_data_count >= 2) {
475*f6aab3d8Srobert if (subcode_label && subcode_desc)
476061da546Spatrick strm.Printf(", %s=%s", subcode_label, subcode_desc);
477*f6aab3d8Srobert else if (subcode_label)
478061da546Spatrick strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode);
479061da546Spatrick }
480061da546Spatrick
481061da546Spatrick if (m_exc_data_count > 0)
482061da546Spatrick strm.PutChar(')');
483061da546Spatrick
484dda28197Spatrick m_description = std::string(strm.GetString());
485061da546Spatrick return m_description.c_str();
486061da546Spatrick }
487061da546Spatrick
GetStopInfoForHardwareBP(Thread & thread,Target * target,uint32_t exc_data_count,uint64_t exc_sub_code,uint64_t exc_sub_sub_code)488dda28197Spatrick static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target,
489dda28197Spatrick uint32_t exc_data_count,
490dda28197Spatrick uint64_t exc_sub_code,
491dda28197Spatrick uint64_t exc_sub_sub_code) {
492dda28197Spatrick // Try hardware watchpoint.
493dda28197Spatrick if (target) {
494dda28197Spatrick // The exc_sub_code indicates the data break address.
495dda28197Spatrick lldb::WatchpointSP wp_sp =
496dda28197Spatrick target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code);
497dda28197Spatrick if (wp_sp && wp_sp->IsEnabled()) {
498dda28197Spatrick // Debugserver may piggyback the hardware index of the fired watchpoint
499dda28197Spatrick // in the exception data. Set the hardware index if that's the case.
500dda28197Spatrick if (exc_data_count >= 3)
501dda28197Spatrick wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
502dda28197Spatrick return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID());
503dda28197Spatrick }
504dda28197Spatrick }
505dda28197Spatrick
506dda28197Spatrick // Try hardware breakpoint.
507dda28197Spatrick ProcessSP process_sp(thread.GetProcess());
508dda28197Spatrick if (process_sp) {
509dda28197Spatrick // The exc_sub_code indicates the data break address.
510dda28197Spatrick lldb::BreakpointSiteSP bp_sp =
511dda28197Spatrick process_sp->GetBreakpointSiteList().FindByAddress(
512dda28197Spatrick (lldb::addr_t)exc_sub_code);
513dda28197Spatrick if (bp_sp && bp_sp->IsEnabled()) {
514dda28197Spatrick // Debugserver may piggyback the hardware index of the fired breakpoint
515dda28197Spatrick // in the exception data. Set the hardware index if that's the case.
516dda28197Spatrick if (exc_data_count >= 3)
517dda28197Spatrick bp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
518dda28197Spatrick return StopInfo::CreateStopReasonWithBreakpointSiteID(thread,
519dda28197Spatrick bp_sp->GetID());
520dda28197Spatrick }
521dda28197Spatrick }
522dda28197Spatrick
523dda28197Spatrick return nullptr;
524dda28197Spatrick }
525dda28197Spatrick
526*f6aab3d8Srobert #if defined(__APPLE__)
527*f6aab3d8Srobert const char *
Name(exception_type_t exc_type)528*f6aab3d8Srobert StopInfoMachException::MachException::Name(exception_type_t exc_type) {
529*f6aab3d8Srobert switch (exc_type) {
530*f6aab3d8Srobert case EXC_BAD_ACCESS:
531*f6aab3d8Srobert return "EXC_BAD_ACCESS";
532*f6aab3d8Srobert case EXC_BAD_INSTRUCTION:
533*f6aab3d8Srobert return "EXC_BAD_INSTRUCTION";
534*f6aab3d8Srobert case EXC_ARITHMETIC:
535*f6aab3d8Srobert return "EXC_ARITHMETIC";
536*f6aab3d8Srobert case EXC_EMULATION:
537*f6aab3d8Srobert return "EXC_EMULATION";
538*f6aab3d8Srobert case EXC_SOFTWARE:
539*f6aab3d8Srobert return "EXC_SOFTWARE";
540*f6aab3d8Srobert case EXC_BREAKPOINT:
541*f6aab3d8Srobert return "EXC_BREAKPOINT";
542*f6aab3d8Srobert case EXC_SYSCALL:
543*f6aab3d8Srobert return "EXC_SYSCALL";
544*f6aab3d8Srobert case EXC_MACH_SYSCALL:
545*f6aab3d8Srobert return "EXC_MACH_SYSCALL";
546*f6aab3d8Srobert case EXC_RPC_ALERT:
547*f6aab3d8Srobert return "EXC_RPC_ALERT";
548*f6aab3d8Srobert #ifdef EXC_CRASH
549*f6aab3d8Srobert case EXC_CRASH:
550*f6aab3d8Srobert return "EXC_CRASH";
551*f6aab3d8Srobert #endif
552*f6aab3d8Srobert case EXC_RESOURCE:
553*f6aab3d8Srobert return "EXC_RESOURCE";
554*f6aab3d8Srobert #ifdef EXC_GUARD
555*f6aab3d8Srobert case EXC_GUARD:
556*f6aab3d8Srobert return "EXC_GUARD";
557*f6aab3d8Srobert #endif
558*f6aab3d8Srobert #ifdef EXC_CORPSE_NOTIFY
559*f6aab3d8Srobert case EXC_CORPSE_NOTIFY:
560*f6aab3d8Srobert return "EXC_CORPSE_NOTIFY";
561*f6aab3d8Srobert #endif
562*f6aab3d8Srobert #ifdef EXC_CORPSE_VARIANT_BIT
563*f6aab3d8Srobert case EXC_CORPSE_VARIANT_BIT:
564*f6aab3d8Srobert return "EXC_CORPSE_VARIANT_BIT";
565*f6aab3d8Srobert #endif
566*f6aab3d8Srobert default:
567*f6aab3d8Srobert break;
568*f6aab3d8Srobert }
569*f6aab3d8Srobert return NULL;
570*f6aab3d8Srobert }
571*f6aab3d8Srobert
572*f6aab3d8Srobert std::optional<exception_type_t>
ExceptionCode(const char * name)573*f6aab3d8Srobert StopInfoMachException::MachException::ExceptionCode(const char *name) {
574*f6aab3d8Srobert return llvm::StringSwitch<std::optional<exception_type_t>>(name)
575*f6aab3d8Srobert .Case("EXC_BAD_ACCESS", EXC_BAD_ACCESS)
576*f6aab3d8Srobert .Case("EXC_BAD_INSTRUCTION", EXC_BAD_INSTRUCTION)
577*f6aab3d8Srobert .Case("EXC_ARITHMETIC", EXC_ARITHMETIC)
578*f6aab3d8Srobert .Case("EXC_EMULATION", EXC_EMULATION)
579*f6aab3d8Srobert .Case("EXC_SOFTWARE", EXC_SOFTWARE)
580*f6aab3d8Srobert .Case("EXC_BREAKPOINT", EXC_BREAKPOINT)
581*f6aab3d8Srobert .Case("EXC_SYSCALL", EXC_SYSCALL)
582*f6aab3d8Srobert .Case("EXC_MACH_SYSCALL", EXC_MACH_SYSCALL)
583*f6aab3d8Srobert .Case("EXC_RPC_ALERT", EXC_RPC_ALERT)
584*f6aab3d8Srobert #ifdef EXC_CRASH
585*f6aab3d8Srobert .Case("EXC_CRASH", EXC_CRASH)
586*f6aab3d8Srobert #endif
587*f6aab3d8Srobert .Case("EXC_RESOURCE", EXC_RESOURCE)
588*f6aab3d8Srobert #ifdef EXC_GUARD
589*f6aab3d8Srobert .Case("EXC_GUARD", EXC_GUARD)
590*f6aab3d8Srobert #endif
591*f6aab3d8Srobert #ifdef EXC_CORPSE_NOTIFY
592*f6aab3d8Srobert .Case("EXC_CORPSE_NOTIFY", EXC_CORPSE_NOTIFY)
593*f6aab3d8Srobert #endif
594*f6aab3d8Srobert .Default(std::nullopt);
595*f6aab3d8Srobert }
596*f6aab3d8Srobert #endif
597*f6aab3d8Srobert
CreateStopReasonWithMachException(Thread & thread,uint32_t exc_type,uint32_t exc_data_count,uint64_t exc_code,uint64_t exc_sub_code,uint64_t exc_sub_sub_code,bool pc_already_adjusted,bool adjust_pc_if_needed)598061da546Spatrick StopInfoSP StopInfoMachException::CreateStopReasonWithMachException(
599061da546Spatrick Thread &thread, uint32_t exc_type, uint32_t exc_data_count,
600061da546Spatrick uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code,
601061da546Spatrick bool pc_already_adjusted, bool adjust_pc_if_needed) {
602061da546Spatrick if (exc_type == 0)
603061da546Spatrick return StopInfoSP();
604061da546Spatrick
605061da546Spatrick uint32_t pc_decrement = 0;
606061da546Spatrick ExecutionContext exe_ctx(thread.shared_from_this());
607061da546Spatrick Target *target = exe_ctx.GetTargetPtr();
608061da546Spatrick const llvm::Triple::ArchType cpu =
609061da546Spatrick target ? target->GetArchitecture().GetMachine()
610061da546Spatrick : llvm::Triple::UnknownArch;
611061da546Spatrick
612061da546Spatrick switch (exc_type) {
613061da546Spatrick case 1: // EXC_BAD_ACCESS
614061da546Spatrick case 2: // EXC_BAD_INSTRUCTION
615061da546Spatrick case 3: // EXC_ARITHMETIC
616061da546Spatrick case 4: // EXC_EMULATION
617061da546Spatrick break;
618061da546Spatrick
619061da546Spatrick case 5: // EXC_SOFTWARE
620061da546Spatrick if (exc_code == 0x10003) // EXC_SOFT_SIGNAL
621061da546Spatrick {
622061da546Spatrick if (exc_sub_code == 5) {
623061da546Spatrick // On MacOSX, a SIGTRAP can signify that a process has called exec,
624061da546Spatrick // so we should check with our dynamic loader to verify.
625061da546Spatrick ProcessSP process_sp(thread.GetProcess());
626061da546Spatrick if (process_sp) {
627061da546Spatrick DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader();
628061da546Spatrick if (dynamic_loader && dynamic_loader->ProcessDidExec()) {
629061da546Spatrick // The program was re-exec'ed
630061da546Spatrick return StopInfo::CreateStopReasonWithExec(thread);
631061da546Spatrick }
632061da546Spatrick }
633061da546Spatrick }
634061da546Spatrick return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code);
635061da546Spatrick }
636061da546Spatrick break;
637061da546Spatrick
638061da546Spatrick case 6: // EXC_BREAKPOINT
639061da546Spatrick {
640061da546Spatrick bool is_actual_breakpoint = false;
641061da546Spatrick bool is_trace_if_actual_breakpoint_missing = false;
642061da546Spatrick switch (cpu) {
643061da546Spatrick case llvm::Triple::x86:
644061da546Spatrick case llvm::Triple::x86_64:
645061da546Spatrick if (exc_code == 1) // EXC_I386_SGL
646061da546Spatrick {
647061da546Spatrick if (!exc_sub_code) {
648061da546Spatrick // This looks like a plain trap.
649061da546Spatrick // Have to check if there is a breakpoint here as well. When you
650061da546Spatrick // single-step onto a trap, the single step stops you not to trap.
651061da546Spatrick // Since we also do that check below, let's just use that logic.
652061da546Spatrick is_actual_breakpoint = true;
653061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
654061da546Spatrick } else {
655dda28197Spatrick if (StopInfoSP stop_info =
656dda28197Spatrick GetStopInfoForHardwareBP(thread, target, exc_data_count,
657dda28197Spatrick exc_sub_code, exc_sub_sub_code))
658dda28197Spatrick return stop_info;
659061da546Spatrick }
660061da546Spatrick } else if (exc_code == 2 || // EXC_I386_BPT
661061da546Spatrick exc_code == 3) // EXC_I386_BPTFLT
662061da546Spatrick {
663061da546Spatrick // KDP returns EXC_I386_BPTFLT for trace breakpoints
664061da546Spatrick if (exc_code == 3)
665061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
666061da546Spatrick
667061da546Spatrick is_actual_breakpoint = true;
668061da546Spatrick if (!pc_already_adjusted)
669061da546Spatrick pc_decrement = 1;
670061da546Spatrick }
671061da546Spatrick break;
672061da546Spatrick
673061da546Spatrick case llvm::Triple::arm:
674061da546Spatrick case llvm::Triple::thumb:
675061da546Spatrick if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
676061da546Spatrick {
677061da546Spatrick // It's a watchpoint, then, if the exc_sub_code indicates a
678061da546Spatrick // known/enabled data break address from our watchpoint list.
679061da546Spatrick lldb::WatchpointSP wp_sp;
680061da546Spatrick if (target)
681061da546Spatrick wp_sp = target->GetWatchpointList().FindByAddress(
682061da546Spatrick (lldb::addr_t)exc_sub_code);
683061da546Spatrick if (wp_sp && wp_sp->IsEnabled()) {
684061da546Spatrick // Debugserver may piggyback the hardware index of the fired
685061da546Spatrick // watchpoint in the exception data. Set the hardware index if
686061da546Spatrick // that's the case.
687061da546Spatrick if (exc_data_count >= 3)
688061da546Spatrick wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
689061da546Spatrick return StopInfo::CreateStopReasonWithWatchpointID(thread,
690061da546Spatrick wp_sp->GetID());
691061da546Spatrick } else {
692061da546Spatrick is_actual_breakpoint = true;
693061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
694061da546Spatrick }
695061da546Spatrick } else if (exc_code == 1) // EXC_ARM_BREAKPOINT
696061da546Spatrick {
697061da546Spatrick is_actual_breakpoint = true;
698061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
699061da546Spatrick } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel
700061da546Spatrick // is currently returning this so accept it
701061da546Spatrick // as indicating a breakpoint until the
702061da546Spatrick // kernel is fixed
703061da546Spatrick {
704061da546Spatrick is_actual_breakpoint = true;
705061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
706061da546Spatrick }
707061da546Spatrick break;
708061da546Spatrick
709061da546Spatrick case llvm::Triple::aarch64_32:
710061da546Spatrick case llvm::Triple::aarch64: {
711061da546Spatrick if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT
712061da546Spatrick {
713061da546Spatrick // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0
714061da546Spatrick // is set
715061da546Spatrick is_actual_breakpoint = false;
716061da546Spatrick is_trace_if_actual_breakpoint_missing = true;
717061da546Spatrick }
718061da546Spatrick if (exc_code == 0x102) // EXC_ARM_DA_DEBUG
719061da546Spatrick {
720061da546Spatrick // It's a watchpoint, then, if the exc_sub_code indicates a
721061da546Spatrick // known/enabled data break address from our watchpoint list.
722061da546Spatrick lldb::WatchpointSP wp_sp;
723061da546Spatrick if (target)
724061da546Spatrick wp_sp = target->GetWatchpointList().FindByAddress(
725061da546Spatrick (lldb::addr_t)exc_sub_code);
726061da546Spatrick if (wp_sp && wp_sp->IsEnabled()) {
727061da546Spatrick // Debugserver may piggyback the hardware index of the fired
728061da546Spatrick // watchpoint in the exception data. Set the hardware index if
729061da546Spatrick // that's the case.
730061da546Spatrick if (exc_data_count >= 3)
731061da546Spatrick wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code);
732061da546Spatrick return StopInfo::CreateStopReasonWithWatchpointID(thread,
733061da546Spatrick wp_sp->GetID());
734061da546Spatrick }
735061da546Spatrick // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as
736061da546Spatrick // EXC_BAD_ACCESS
737061da546Spatrick if (thread.GetTemporaryResumeState() == eStateStepping)
738061da546Spatrick return StopInfo::CreateStopReasonToTrace(thread);
739061da546Spatrick }
740061da546Spatrick // It looks like exc_sub_code has the 4 bytes of the instruction that
741061da546Spatrick // triggered the exception, i.e. our breakpoint opcode
742061da546Spatrick is_actual_breakpoint = exc_code == 1;
743061da546Spatrick break;
744061da546Spatrick }
745061da546Spatrick
746061da546Spatrick default:
747061da546Spatrick break;
748061da546Spatrick }
749061da546Spatrick
750061da546Spatrick if (is_actual_breakpoint) {
751061da546Spatrick RegisterContextSP reg_ctx_sp(thread.GetRegisterContext());
752061da546Spatrick addr_t pc = reg_ctx_sp->GetPC() - pc_decrement;
753061da546Spatrick
754061da546Spatrick ProcessSP process_sp(thread.CalculateProcess());
755061da546Spatrick
756061da546Spatrick lldb::BreakpointSiteSP bp_site_sp;
757061da546Spatrick if (process_sp)
758061da546Spatrick bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc);
759061da546Spatrick if (bp_site_sp && bp_site_sp->IsEnabled()) {
760061da546Spatrick // Update the PC if we were asked to do so, but only do so if we find
761061da546Spatrick // a breakpoint that we know about cause this could be a trap
762061da546Spatrick // instruction in the code
763061da546Spatrick if (pc_decrement > 0 && adjust_pc_if_needed)
764061da546Spatrick reg_ctx_sp->SetPC(pc);
765061da546Spatrick
766061da546Spatrick // If the breakpoint is for this thread, then we'll report the hit,
767061da546Spatrick // but if it is for another thread, we can just report no reason. We
768061da546Spatrick // don't need to worry about stepping over the breakpoint here, that
769061da546Spatrick // will be taken care of when the thread resumes and notices that
770061da546Spatrick // there's a breakpoint under the pc. If we have an operating system
771061da546Spatrick // plug-in, we might have set a thread specific breakpoint using the
772061da546Spatrick // operating system thread ID, so we can't make any assumptions about
773061da546Spatrick // the thread ID so we must always report the breakpoint regardless
774061da546Spatrick // of the thread.
775be691f3bSpatrick if (bp_site_sp->ValidForThisThread(thread) ||
776061da546Spatrick thread.GetProcess()->GetOperatingSystem() != nullptr)
777061da546Spatrick return StopInfo::CreateStopReasonWithBreakpointSiteID(
778061da546Spatrick thread, bp_site_sp->GetID());
779061da546Spatrick else if (is_trace_if_actual_breakpoint_missing)
780061da546Spatrick return StopInfo::CreateStopReasonToTrace(thread);
781061da546Spatrick else
782061da546Spatrick return StopInfoSP();
783061da546Spatrick }
784061da546Spatrick
785061da546Spatrick // Don't call this a trace if we weren't single stepping this thread.
786061da546Spatrick if (is_trace_if_actual_breakpoint_missing &&
787061da546Spatrick thread.GetTemporaryResumeState() == eStateStepping) {
788061da546Spatrick return StopInfo::CreateStopReasonToTrace(thread);
789061da546Spatrick }
790061da546Spatrick }
791061da546Spatrick } break;
792061da546Spatrick
793061da546Spatrick case 7: // EXC_SYSCALL
794061da546Spatrick case 8: // EXC_MACH_SYSCALL
795061da546Spatrick case 9: // EXC_RPC_ALERT
796061da546Spatrick case 10: // EXC_CRASH
797061da546Spatrick break;
798061da546Spatrick }
799061da546Spatrick
800061da546Spatrick return StopInfoSP(new StopInfoMachException(thread, exc_type, exc_data_count,
801061da546Spatrick exc_code, exc_sub_code));
802061da546Spatrick }
803