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