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