xref: /freebsd-src/contrib/llvm-project/lldb/source/Symbol/UnwindPlan.cpp (revision c9ccf3a32da427475985b85d7df023ccfb138c27)
1 //===-- UnwindPlan.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 "lldb/Symbol/UnwindPlan.h"
10 
11 #include "lldb/Target/Process.h"
12 #include "lldb/Target/RegisterContext.h"
13 #include "lldb/Target/Target.h"
14 #include "lldb/Target/Thread.h"
15 #include "lldb/Utility/ConstString.h"
16 #include "lldb/Utility/Log.h"
17 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
18 
19 using namespace lldb;
20 using namespace lldb_private;
21 
22 bool UnwindPlan::Row::RegisterLocation::
23 operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
24   if (m_type == rhs.m_type) {
25     switch (m_type) {
26     case unspecified:
27     case undefined:
28     case same:
29       return true;
30 
31     case atCFAPlusOffset:
32     case isCFAPlusOffset:
33     case atAFAPlusOffset:
34     case isAFAPlusOffset:
35       return m_location.offset == rhs.m_location.offset;
36 
37     case inOtherRegister:
38       return m_location.reg_num == rhs.m_location.reg_num;
39 
40     case atDWARFExpression:
41     case isDWARFExpression:
42       if (m_location.expr.length == rhs.m_location.expr.length)
43         return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
44                        m_location.expr.length);
45       break;
46     }
47   }
48   return false;
49 }
50 
51 // This function doesn't copy the dwarf expression bytes; they must remain in
52 // allocated memory for the lifespan of this UnwindPlan object.
53 void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
54     const uint8_t *opcodes, uint32_t len) {
55   m_type = atDWARFExpression;
56   m_location.expr.opcodes = opcodes;
57   m_location.expr.length = len;
58 }
59 
60 // This function doesn't copy the dwarf expression bytes; they must remain in
61 // allocated memory for the lifespan of this UnwindPlan object.
62 void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
63     const uint8_t *opcodes, uint32_t len) {
64   m_type = isDWARFExpression;
65   m_location.expr.opcodes = opcodes;
66   m_location.expr.length = len;
67 }
68 
69 static llvm::Optional<std::pair<lldb::ByteOrder, uint32_t>>
70 GetByteOrderAndAddrSize(Thread *thread) {
71   if (!thread)
72     return llvm::None;
73   ProcessSP process_sp = thread->GetProcess();
74   if (!process_sp)
75     return llvm::None;
76   ArchSpec arch = process_sp->GetTarget().GetArchitecture();
77   return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
78 }
79 
80 static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
81   if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
82     llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,
83                              order_and_width->second);
84     llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32)
85         .print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr, nullptr);
86   } else
87     s.PutCString("dwarf-expr");
88 }
89 
90 void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
91                                              const UnwindPlan *unwind_plan,
92                                              const UnwindPlan::Row *row,
93                                              Thread *thread,
94                                              bool verbose) const {
95   switch (m_type) {
96   case unspecified:
97     if (verbose)
98       s.PutCString("=<unspec>");
99     else
100       s.PutCString("=!");
101     break;
102   case undefined:
103     if (verbose)
104       s.PutCString("=<undef>");
105     else
106       s.PutCString("=?");
107     break;
108   case same:
109     s.PutCString("= <same>");
110     break;
111 
112   case atCFAPlusOffset:
113   case isCFAPlusOffset: {
114     s.PutChar('=');
115     if (m_type == atCFAPlusOffset)
116       s.PutChar('[');
117     s.Printf("CFA%+d", m_location.offset);
118     if (m_type == atCFAPlusOffset)
119       s.PutChar(']');
120   } break;
121 
122   case atAFAPlusOffset:
123   case isAFAPlusOffset: {
124     s.PutChar('=');
125     if (m_type == atAFAPlusOffset)
126       s.PutChar('[');
127     s.Printf("AFA%+d", m_location.offset);
128     if (m_type == atAFAPlusOffset)
129       s.PutChar(']');
130   } break;
131 
132   case inOtherRegister: {
133     const RegisterInfo *other_reg_info = nullptr;
134     if (unwind_plan)
135       other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
136     if (other_reg_info)
137       s.Printf("=%s", other_reg_info->name);
138     else
139       s.Printf("=reg(%u)", m_location.reg_num);
140   } break;
141 
142   case atDWARFExpression:
143   case isDWARFExpression: {
144     s.PutChar('=');
145     if (m_type == atDWARFExpression)
146       s.PutChar('[');
147     DumpDWARFExpr(
148         s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
149         thread);
150     if (m_type == atDWARFExpression)
151       s.PutChar(']');
152   } break;
153   }
154 }
155 
156 static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
157                              Thread *thread, uint32_t reg_num) {
158   const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
159   if (reg_info)
160     s.PutCString(reg_info->name);
161   else
162     s.Printf("reg(%u)", reg_num);
163 }
164 
165 bool UnwindPlan::Row::FAValue::
166 operator==(const UnwindPlan::Row::FAValue &rhs) const {
167   if (m_type == rhs.m_type) {
168     switch (m_type) {
169     case unspecified:
170     case isRaSearch:
171       return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
172 
173     case isRegisterPlusOffset:
174       return m_value.reg.offset == rhs.m_value.reg.offset;
175 
176     case isRegisterDereferenced:
177       return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
178 
179     case isDWARFExpression:
180       if (m_value.expr.length == rhs.m_value.expr.length)
181         return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
182                        m_value.expr.length);
183       break;
184     }
185   }
186   return false;
187 }
188 
189 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
190                                      Thread *thread) const {
191   switch (m_type) {
192   case isRegisterPlusOffset:
193     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
194     s.Printf("%+3d", m_value.reg.offset);
195     break;
196   case isRegisterDereferenced:
197     s.PutChar('[');
198     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
199     s.PutChar(']');
200     break;
201   case isDWARFExpression:
202     DumpDWARFExpr(s,
203                   llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
204                   thread);
205     break;
206   case unspecified:
207     s.PutCString("unspecified");
208     break;
209   case isRaSearch:
210     s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
211     break;
212   }
213 }
214 
215 void UnwindPlan::Row::Clear() {
216   m_cfa_value.SetUnspecified();
217   m_afa_value.SetUnspecified();
218   m_offset = 0;
219   m_unspecified_registers_are_undefined = false;
220   m_register_locations.clear();
221 }
222 
223 void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
224                            Thread *thread, addr_t base_addr) const {
225   if (base_addr != LLDB_INVALID_ADDRESS)
226     s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
227   else
228     s.Printf("%4" PRId64 ": CFA=", GetOffset());
229 
230   m_cfa_value.Dump(s, unwind_plan, thread);
231 
232   if (!m_afa_value.IsUnspecified()) {
233     s.Printf(" AFA=");
234     m_afa_value.Dump(s, unwind_plan, thread);
235   }
236 
237   s.Printf(" => ");
238   for (collection::const_iterator idx = m_register_locations.begin();
239        idx != m_register_locations.end(); ++idx) {
240     DumpRegisterName(s, unwind_plan, thread, idx->first);
241     const bool verbose = false;
242     idx->second.Dump(s, unwind_plan, this, thread, verbose);
243     s.PutChar(' ');
244   }
245 }
246 
247 UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
248 
249 bool UnwindPlan::Row::GetRegisterInfo(
250     uint32_t reg_num,
251     UnwindPlan::Row::RegisterLocation &register_location) const {
252   collection::const_iterator pos = m_register_locations.find(reg_num);
253   if (pos != m_register_locations.end()) {
254     register_location = pos->second;
255     return true;
256   }
257   if (m_unspecified_registers_are_undefined) {
258     register_location.SetUndefined();
259     return true;
260   }
261   return false;
262 }
263 
264 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
265   collection::const_iterator pos = m_register_locations.find(reg_num);
266   if (pos != m_register_locations.end()) {
267     m_register_locations.erase(pos);
268   }
269 }
270 
271 void UnwindPlan::Row::SetRegisterInfo(
272     uint32_t reg_num,
273     const UnwindPlan::Row::RegisterLocation register_location) {
274   m_register_locations[reg_num] = register_location;
275 }
276 
277 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
278                                                            int32_t offset,
279                                                            bool can_replace) {
280   if (!can_replace &&
281       m_register_locations.find(reg_num) != m_register_locations.end())
282     return false;
283   RegisterLocation reg_loc;
284   reg_loc.SetAtCFAPlusOffset(offset);
285   m_register_locations[reg_num] = reg_loc;
286   return true;
287 }
288 
289 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
290                                                            int32_t offset,
291                                                            bool can_replace) {
292   if (!can_replace &&
293       m_register_locations.find(reg_num) != m_register_locations.end())
294     return false;
295   RegisterLocation reg_loc;
296   reg_loc.SetIsCFAPlusOffset(offset);
297   m_register_locations[reg_num] = reg_loc;
298   return true;
299 }
300 
301 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
302     uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
303   collection::iterator pos = m_register_locations.find(reg_num);
304   collection::iterator end = m_register_locations.end();
305 
306   if (pos != end) {
307     if (!can_replace)
308       return false;
309     if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
310       return false;
311   }
312   RegisterLocation reg_loc;
313   reg_loc.SetUndefined();
314   m_register_locations[reg_num] = reg_loc;
315   return true;
316 }
317 
318 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
319                                                        bool can_replace) {
320   if (!can_replace &&
321       m_register_locations.find(reg_num) != m_register_locations.end())
322     return false;
323   RegisterLocation reg_loc;
324   reg_loc.SetUnspecified();
325   m_register_locations[reg_num] = reg_loc;
326   return true;
327 }
328 
329 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
330                                                     uint32_t other_reg_num,
331                                                     bool can_replace) {
332   if (!can_replace &&
333       m_register_locations.find(reg_num) != m_register_locations.end())
334     return false;
335   RegisterLocation reg_loc;
336   reg_loc.SetInRegister(other_reg_num);
337   m_register_locations[reg_num] = reg_loc;
338   return true;
339 }
340 
341 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
342                                                 bool must_replace) {
343   if (must_replace &&
344       m_register_locations.find(reg_num) == m_register_locations.end())
345     return false;
346   RegisterLocation reg_loc;
347   reg_loc.SetSame();
348   m_register_locations[reg_num] = reg_loc;
349   return true;
350 }
351 
352 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
353   return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&
354          m_afa_value == rhs.m_afa_value &&
355          m_unspecified_registers_are_undefined ==
356              rhs.m_unspecified_registers_are_undefined &&
357          m_register_locations == rhs.m_register_locations;
358 }
359 
360 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
361   if (m_row_list.empty() ||
362       m_row_list.back()->GetOffset() != row_sp->GetOffset())
363     m_row_list.push_back(row_sp);
364   else
365     m_row_list.back() = row_sp;
366 }
367 
368 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
369                            bool replace_existing) {
370   collection::iterator it = m_row_list.begin();
371   while (it != m_row_list.end()) {
372     RowSP row = *it;
373     if (row->GetOffset() >= row_sp->GetOffset())
374       break;
375     it++;
376   }
377   if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
378     m_row_list.insert(it, row_sp);
379   else if (replace_existing)
380     *it = row_sp;
381 }
382 
383 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
384   RowSP row;
385   if (!m_row_list.empty()) {
386     if (offset == -1)
387       row = m_row_list.back();
388     else {
389       collection::const_iterator pos, end = m_row_list.end();
390       for (pos = m_row_list.begin(); pos != end; ++pos) {
391         if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
392           row = *pos;
393         else
394           break;
395       }
396     }
397   }
398   return row;
399 }
400 
401 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
402   return idx < m_row_list.size();
403 }
404 
405 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
406   if (idx < m_row_list.size())
407     return m_row_list[idx];
408   else {
409     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
410     LLDB_LOGF(log,
411               "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
412               "(number rows is %u)",
413               idx, (uint32_t)m_row_list.size());
414     return UnwindPlan::RowSP();
415   }
416 }
417 
418 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
419   if (m_row_list.empty()) {
420     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
421     LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
422     return UnwindPlan::RowSP();
423   }
424   return m_row_list.back();
425 }
426 
427 int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
428 
429 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
430   if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
431     m_plan_valid_address_range = range;
432 }
433 
434 bool UnwindPlan::PlanValidAtAddress(Address addr) {
435   // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
436   if (GetRowCount() == 0) {
437     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
438     if (log) {
439       StreamString s;
440       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
441         LLDB_LOGF(log,
442                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
443                   "'%s' at address %s",
444                   m_source_name.GetCString(), s.GetData());
445       } else {
446         LLDB_LOGF(log,
447                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
448                   m_source_name.GetCString());
449       }
450     }
451     return false;
452   }
453 
454   // If the 0th Row of unwind instructions is missing, or if it doesn't provide
455   // a register to use to find the Canonical Frame Address, this is not a valid
456   // UnwindPlan.
457   if (GetRowAtIndex(0).get() == nullptr ||
458       GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
459           Row::FAValue::unspecified) {
460     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
461     if (log) {
462       StreamString s;
463       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
464         LLDB_LOGF(log,
465                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
466                   "for UnwindPlan '%s' at address %s",
467                   m_source_name.GetCString(), s.GetData());
468       } else {
469         LLDB_LOGF(log,
470                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
471                   "for UnwindPlan '%s'",
472                   m_source_name.GetCString());
473       }
474     }
475     return false;
476   }
477 
478   if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
479       m_plan_valid_address_range.GetByteSize() == 0)
480     return true;
481 
482   if (!addr.IsValid())
483     return true;
484 
485   if (m_plan_valid_address_range.ContainsFileAddress(addr))
486     return true;
487 
488   return false;
489 }
490 
491 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
492   if (!m_source_name.IsEmpty()) {
493     s.Printf("This UnwindPlan originally sourced from %s\n",
494              m_source_name.GetCString());
495   }
496   if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
497     TargetSP target_sp(thread->CalculateTarget());
498     addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
499     addr_t personality_func_load_addr =
500         m_personality_func_addr.GetLoadAddress(target_sp.get());
501 
502     if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
503         personality_func_load_addr != LLDB_INVALID_ADDRESS) {
504       s.Printf("LSDA address 0x%" PRIx64
505                ", personality routine is at address 0x%" PRIx64 "\n",
506                lsda_load_addr, personality_func_load_addr);
507     }
508   }
509   s.Printf("This UnwindPlan is sourced from the compiler: ");
510   switch (m_plan_is_sourced_from_compiler) {
511   case eLazyBoolYes:
512     s.Printf("yes.\n");
513     break;
514   case eLazyBoolNo:
515     s.Printf("no.\n");
516     break;
517   case eLazyBoolCalculate:
518     s.Printf("not specified.\n");
519     break;
520   }
521   s.Printf("This UnwindPlan is valid at all instruction locations: ");
522   switch (m_plan_is_valid_at_all_instruction_locations) {
523   case eLazyBoolYes:
524     s.Printf("yes.\n");
525     break;
526   case eLazyBoolNo:
527     s.Printf("no.\n");
528     break;
529   case eLazyBoolCalculate:
530     s.Printf("not specified.\n");
531     break;
532   }
533   s.Printf("This UnwindPlan is for a trap handler function: ");
534   switch (m_plan_is_for_signal_trap) {
535   case eLazyBoolYes:
536     s.Printf("yes.\n");
537     break;
538   case eLazyBoolNo:
539     s.Printf("no.\n");
540     break;
541   case eLazyBoolCalculate:
542     s.Printf("not specified.\n");
543     break;
544   }
545   if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
546       m_plan_valid_address_range.GetByteSize() > 0) {
547     s.PutCString("Address range of this UnwindPlan: ");
548     TargetSP target_sp(thread->CalculateTarget());
549     m_plan_valid_address_range.Dump(&s, target_sp.get(),
550                                     Address::DumpStyleSectionNameOffset);
551     s.EOL();
552   }
553   collection::const_iterator pos, begin = m_row_list.begin(),
554                                   end = m_row_list.end();
555   for (pos = begin; pos != end; ++pos) {
556     s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
557     (*pos)->Dump(s, this, thread, base_addr);
558     s.Printf("\n");
559   }
560 }
561 
562 void UnwindPlan::SetSourceName(const char *source) {
563   m_source_name = ConstString(source);
564 }
565 
566 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
567 
568 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
569                                                 uint32_t unwind_reg) const {
570   if (thread) {
571     RegisterContext *reg_ctx = thread->GetRegisterContext().get();
572     if (reg_ctx) {
573       uint32_t reg;
574       if (m_register_kind == eRegisterKindLLDB)
575         reg = unwind_reg;
576       else
577         reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
578                                                            unwind_reg);
579       if (reg != LLDB_INVALID_REGNUM)
580         return reg_ctx->GetRegisterInfoAtIndex(reg);
581     }
582   }
583   return nullptr;
584 }
585