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