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