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/Expression/DWARFExpression.h"
12 #include "lldb/Target/Process.h"
13 #include "lldb/Target/RegisterContext.h"
14 #include "lldb/Target/Target.h"
15 #include "lldb/Target/Thread.h"
16 #include "lldb/Utility/ConstString.h"
17 #include "lldb/Utility/Log.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     DataExtractor extractor(expr.data(), expr.size(), order_and_width->first,
83                             order_and_width->second);
84     if (!DWARFExpression::PrintDWARFExpression(s, extractor,
85                                                order_and_width->second,
86                                                /*dwarf_ref_size*/ 4,
87                                                /*location_expression*/ false))
88       s.PutCString("invalid-dwarf-expr");
89   } else
90     s.PutCString("dwarf-expr");
91 }
92 
93 void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
94                                              const UnwindPlan *unwind_plan,
95                                              const UnwindPlan::Row *row,
96                                              Thread *thread,
97                                              bool verbose) const {
98   switch (m_type) {
99   case unspecified:
100     if (verbose)
101       s.PutCString("=<unspec>");
102     else
103       s.PutCString("=!");
104     break;
105   case undefined:
106     if (verbose)
107       s.PutCString("=<undef>");
108     else
109       s.PutCString("=?");
110     break;
111   case same:
112     s.PutCString("= <same>");
113     break;
114 
115   case atCFAPlusOffset:
116   case isCFAPlusOffset: {
117     s.PutChar('=');
118     if (m_type == atCFAPlusOffset)
119       s.PutChar('[');
120     s.Printf("CFA%+d", m_location.offset);
121     if (m_type == atCFAPlusOffset)
122       s.PutChar(']');
123   } break;
124 
125   case atAFAPlusOffset:
126   case isAFAPlusOffset: {
127     s.PutChar('=');
128     if (m_type == atAFAPlusOffset)
129       s.PutChar('[');
130     s.Printf("AFA%+d", m_location.offset);
131     if (m_type == atAFAPlusOffset)
132       s.PutChar(']');
133   } break;
134 
135   case inOtherRegister: {
136     const RegisterInfo *other_reg_info = nullptr;
137     if (unwind_plan)
138       other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
139     if (other_reg_info)
140       s.Printf("=%s", other_reg_info->name);
141     else
142       s.Printf("=reg(%u)", m_location.reg_num);
143   } break;
144 
145   case atDWARFExpression:
146   case isDWARFExpression: {
147     s.PutChar('=');
148     if (m_type == atDWARFExpression)
149       s.PutChar('[');
150     DumpDWARFExpr(
151         s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
152         thread);
153     if (m_type == atDWARFExpression)
154       s.PutChar(']');
155   } break;
156   }
157 }
158 
159 static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
160                              Thread *thread, uint32_t reg_num) {
161   const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
162   if (reg_info)
163     s.PutCString(reg_info->name);
164   else
165     s.Printf("reg(%u)", reg_num);
166 }
167 
168 bool UnwindPlan::Row::FAValue::
169 operator==(const UnwindPlan::Row::FAValue &rhs) const {
170   if (m_type == rhs.m_type) {
171     switch (m_type) {
172     case unspecified:
173     case isRaSearch:
174       return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
175 
176     case isRegisterPlusOffset:
177       return m_value.reg.offset == rhs.m_value.reg.offset;
178 
179     case isRegisterDereferenced:
180       return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
181 
182     case isDWARFExpression:
183       if (m_value.expr.length == rhs.m_value.expr.length)
184         return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
185                        m_value.expr.length);
186       break;
187     }
188   }
189   return false;
190 }
191 
192 void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
193                                      Thread *thread) const {
194   switch (m_type) {
195   case isRegisterPlusOffset:
196     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
197     s.Printf("%+3d", m_value.reg.offset);
198     break;
199   case isRegisterDereferenced:
200     s.PutChar('[');
201     DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
202     s.PutChar(']');
203     break;
204   case isDWARFExpression:
205     DumpDWARFExpr(s,
206                   llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
207                   thread);
208     break;
209   case unspecified:
210     s.PutCString("unspecified");
211     break;
212   case isRaSearch:
213     s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
214     break;
215   }
216 }
217 
218 void UnwindPlan::Row::Clear() {
219   m_cfa_value.SetUnspecified();
220   m_afa_value.SetUnspecified();
221   m_offset = 0;
222   m_register_locations.clear();
223 }
224 
225 void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
226                            Thread *thread, addr_t base_addr) const {
227   if (base_addr != LLDB_INVALID_ADDRESS)
228     s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
229   else
230     s.Printf("%4" PRId64 ": CFA=", GetOffset());
231 
232   m_cfa_value.Dump(s, unwind_plan, thread);
233 
234   if (!m_afa_value.IsUnspecified()) {
235     s.Printf(" AFA=");
236     m_afa_value.Dump(s, unwind_plan, thread);
237   }
238 
239   s.Printf(" => ");
240   for (collection::const_iterator idx = m_register_locations.begin();
241        idx != m_register_locations.end(); ++idx) {
242     DumpRegisterName(s, unwind_plan, thread, idx->first);
243     const bool verbose = false;
244     idx->second.Dump(s, unwind_plan, this, thread, verbose);
245     s.PutChar(' ');
246   }
247   s.EOL();
248 }
249 
250 UnwindPlan::Row::Row()
251     : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {}
252 
253 bool UnwindPlan::Row::GetRegisterInfo(
254     uint32_t reg_num,
255     UnwindPlan::Row::RegisterLocation &register_location) const {
256   collection::const_iterator pos = m_register_locations.find(reg_num);
257   if (pos != m_register_locations.end()) {
258     register_location = pos->second;
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 &&
354       m_cfa_value == rhs.m_cfa_value &&
355       m_afa_value == rhs.m_afa_value &&
356       m_register_locations == rhs.m_register_locations;
357 }
358 
359 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
360   if (m_row_list.empty() ||
361       m_row_list.back()->GetOffset() != row_sp->GetOffset())
362     m_row_list.push_back(row_sp);
363   else
364     m_row_list.back() = row_sp;
365 }
366 
367 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
368                            bool replace_existing) {
369   collection::iterator it = m_row_list.begin();
370   while (it != m_row_list.end()) {
371     RowSP row = *it;
372     if (row->GetOffset() >= row_sp->GetOffset())
373       break;
374     it++;
375   }
376   if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
377     m_row_list.insert(it, row_sp);
378   else if (replace_existing)
379     *it = row_sp;
380 }
381 
382 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
383   RowSP row;
384   if (!m_row_list.empty()) {
385     if (offset == -1)
386       row = m_row_list.back();
387     else {
388       collection::const_iterator pos, end = m_row_list.end();
389       for (pos = m_row_list.begin(); pos != end; ++pos) {
390         if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
391           row = *pos;
392         else
393           break;
394       }
395     }
396   }
397   return row;
398 }
399 
400 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
401   return idx < m_row_list.size();
402 }
403 
404 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
405   if (idx < m_row_list.size())
406     return m_row_list[idx];
407   else {
408     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
409     LLDB_LOGF(log,
410               "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
411               "(number rows is %u)",
412               idx, (uint32_t)m_row_list.size());
413     return UnwindPlan::RowSP();
414   }
415 }
416 
417 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
418   if (m_row_list.empty()) {
419     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
420     LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
421     return UnwindPlan::RowSP();
422   }
423   return m_row_list.back();
424 }
425 
426 int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
427 
428 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
429   if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
430     m_plan_valid_address_range = range;
431 }
432 
433 bool UnwindPlan::PlanValidAtAddress(Address addr) {
434   // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
435   if (GetRowCount() == 0) {
436     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
437     if (log) {
438       StreamString s;
439       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
440         LLDB_LOGF(log,
441                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
442                   "'%s' at address %s",
443                   m_source_name.GetCString(), s.GetData());
444       } else {
445         LLDB_LOGF(log,
446                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
447                   m_source_name.GetCString());
448       }
449     }
450     return false;
451   }
452 
453   // If the 0th Row of unwind instructions is missing, or if it doesn't provide
454   // a register to use to find the Canonical Frame Address, this is not a valid
455   // UnwindPlan.
456   if (GetRowAtIndex(0).get() == nullptr ||
457       GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
458           Row::FAValue::unspecified) {
459     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
460     if (log) {
461       StreamString s;
462       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
463         LLDB_LOGF(log,
464                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
465                   "for UnwindPlan '%s' at address %s",
466                   m_source_name.GetCString(), s.GetData());
467       } else {
468         LLDB_LOGF(log,
469                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
470                   "for UnwindPlan '%s'",
471                   m_source_name.GetCString());
472       }
473     }
474     return false;
475   }
476 
477   if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
478       m_plan_valid_address_range.GetByteSize() == 0)
479     return true;
480 
481   if (!addr.IsValid())
482     return true;
483 
484   if (m_plan_valid_address_range.ContainsFileAddress(addr))
485     return true;
486 
487   return false;
488 }
489 
490 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
491   if (!m_source_name.IsEmpty()) {
492     s.Printf("This UnwindPlan originally sourced from %s\n",
493              m_source_name.GetCString());
494   }
495   if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
496     TargetSP target_sp(thread->CalculateTarget());
497     addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
498     addr_t personality_func_load_addr =
499         m_personality_func_addr.GetLoadAddress(target_sp.get());
500 
501     if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
502         personality_func_load_addr != LLDB_INVALID_ADDRESS) {
503       s.Printf("LSDA address 0x%" PRIx64
504                ", personality routine is at address 0x%" PRIx64 "\n",
505                lsda_load_addr, personality_func_load_addr);
506     }
507   }
508   s.Printf("This UnwindPlan is sourced from the compiler: ");
509   switch (m_plan_is_sourced_from_compiler) {
510   case eLazyBoolYes:
511     s.Printf("yes.\n");
512     break;
513   case eLazyBoolNo:
514     s.Printf("no.\n");
515     break;
516   case eLazyBoolCalculate:
517     s.Printf("not specified.\n");
518     break;
519   }
520   s.Printf("This UnwindPlan is valid at all instruction locations: ");
521   switch (m_plan_is_valid_at_all_instruction_locations) {
522   case eLazyBoolYes:
523     s.Printf("yes.\n");
524     break;
525   case eLazyBoolNo:
526     s.Printf("no.\n");
527     break;
528   case eLazyBoolCalculate:
529     s.Printf("not specified.\n");
530     break;
531   }
532   if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
533       m_plan_valid_address_range.GetByteSize() > 0) {
534     s.PutCString("Address range of this UnwindPlan: ");
535     TargetSP target_sp(thread->CalculateTarget());
536     m_plan_valid_address_range.Dump(&s, target_sp.get(),
537                                     Address::DumpStyleSectionNameOffset);
538     s.EOL();
539   }
540   collection::const_iterator pos, begin = m_row_list.begin(),
541                                   end = m_row_list.end();
542   for (pos = begin; pos != end; ++pos) {
543     s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
544     (*pos)->Dump(s, this, thread, base_addr);
545   }
546 }
547 
548 void UnwindPlan::SetSourceName(const char *source) {
549   m_source_name = ConstString(source);
550 }
551 
552 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
553 
554 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
555                                                 uint32_t unwind_reg) const {
556   if (thread) {
557     RegisterContext *reg_ctx = thread->GetRegisterContext().get();
558     if (reg_ctx) {
559       uint32_t reg;
560       if (m_register_kind == eRegisterKindLLDB)
561         reg = unwind_reg;
562       else
563         reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
564                                                            unwind_reg);
565       if (reg != LLDB_INVALID_REGNUM)
566         return reg_ctx->GetRegisterInfoAtIndex(reg);
567     }
568   }
569   return nullptr;
570 }
571