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/LLDBLog.h"
17 #include "lldb/Utility/Log.h"
18 #include "llvm/DebugInfo/DIContext.h"
19 #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
20 #include <optional>
21 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 bool UnwindPlan::Row::RegisterLocation::
26 operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
27   if (m_type == rhs.m_type) {
28     switch (m_type) {
29     case unspecified:
30     case undefined:
31     case same:
32       return true;
33 
34     case atCFAPlusOffset:
35     case isCFAPlusOffset:
36     case atAFAPlusOffset:
37     case isAFAPlusOffset:
38       return m_location.offset == rhs.m_location.offset;
39 
40     case inOtherRegister:
41       return m_location.reg_num == rhs.m_location.reg_num;
42 
43     case atDWARFExpression:
44     case isDWARFExpression:
45       if (m_location.expr.length == rhs.m_location.expr.length)
46         return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
47                        m_location.expr.length);
48       break;
49     }
50   }
51   return false;
52 }
53 
54 // This function doesn't copy the dwarf expression bytes; they must remain in
55 // allocated memory for the lifespan of this UnwindPlan object.
56 void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
57     const uint8_t *opcodes, uint32_t len) {
58   m_type = atDWARFExpression;
59   m_location.expr.opcodes = opcodes;
60   m_location.expr.length = len;
61 }
62 
63 // This function doesn't copy the dwarf expression bytes; they must remain in
64 // allocated memory for the lifespan of this UnwindPlan object.
65 void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
66     const uint8_t *opcodes, uint32_t len) {
67   m_type = isDWARFExpression;
68   m_location.expr.opcodes = opcodes;
69   m_location.expr.length = len;
70 }
71 
72 static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
73 GetByteOrderAndAddrSize(Thread *thread) {
74   if (!thread)
75     return std::nullopt;
76   ProcessSP process_sp = thread->GetProcess();
77   if (!process_sp)
78     return std::nullopt;
79   ArchSpec arch = process_sp->GetTarget().GetArchitecture();
80   return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
81 }
82 
83 static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
84   if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
85     llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,
86                              order_and_width->second);
87     llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32)
88         .print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
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::ArrayRef(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, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),
206                   thread);
207     break;
208   case unspecified:
209     s.PutCString("unspecified");
210     break;
211   case isRaSearch:
212     s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
213     break;
214   }
215 }
216 
217 void UnwindPlan::Row::Clear() {
218   m_cfa_value.SetUnspecified();
219   m_afa_value.SetUnspecified();
220   m_offset = 0;
221   m_unspecified_registers_are_undefined = false;
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 }
248 
249 UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
250 
251 bool UnwindPlan::Row::GetRegisterInfo(
252     uint32_t reg_num,
253     UnwindPlan::Row::RegisterLocation &register_location) const {
254   collection::const_iterator pos = m_register_locations.find(reg_num);
255   if (pos != m_register_locations.end()) {
256     register_location = pos->second;
257     return true;
258   }
259   if (m_unspecified_registers_are_undefined) {
260     register_location.SetUndefined();
261     return true;
262   }
263   return false;
264 }
265 
266 void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
267   collection::const_iterator pos = m_register_locations.find(reg_num);
268   if (pos != m_register_locations.end()) {
269     m_register_locations.erase(pos);
270   }
271 }
272 
273 void UnwindPlan::Row::SetRegisterInfo(
274     uint32_t reg_num,
275     const UnwindPlan::Row::RegisterLocation register_location) {
276   m_register_locations[reg_num] = register_location;
277 }
278 
279 bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
280                                                            int32_t offset,
281                                                            bool can_replace) {
282   if (!can_replace &&
283       m_register_locations.find(reg_num) != m_register_locations.end())
284     return false;
285   RegisterLocation reg_loc;
286   reg_loc.SetAtCFAPlusOffset(offset);
287   m_register_locations[reg_num] = reg_loc;
288   return true;
289 }
290 
291 bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
292                                                            int32_t offset,
293                                                            bool can_replace) {
294   if (!can_replace &&
295       m_register_locations.find(reg_num) != m_register_locations.end())
296     return false;
297   RegisterLocation reg_loc;
298   reg_loc.SetIsCFAPlusOffset(offset);
299   m_register_locations[reg_num] = reg_loc;
300   return true;
301 }
302 
303 bool UnwindPlan::Row::SetRegisterLocationToUndefined(
304     uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
305   collection::iterator pos = m_register_locations.find(reg_num);
306   collection::iterator end = m_register_locations.end();
307 
308   if (pos != end) {
309     if (!can_replace)
310       return false;
311     if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
312       return false;
313   }
314   RegisterLocation reg_loc;
315   reg_loc.SetUndefined();
316   m_register_locations[reg_num] = reg_loc;
317   return true;
318 }
319 
320 bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
321                                                        bool can_replace) {
322   if (!can_replace &&
323       m_register_locations.find(reg_num) != m_register_locations.end())
324     return false;
325   RegisterLocation reg_loc;
326   reg_loc.SetUnspecified();
327   m_register_locations[reg_num] = reg_loc;
328   return true;
329 }
330 
331 bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
332                                                     uint32_t other_reg_num,
333                                                     bool can_replace) {
334   if (!can_replace &&
335       m_register_locations.find(reg_num) != m_register_locations.end())
336     return false;
337   RegisterLocation reg_loc;
338   reg_loc.SetInRegister(other_reg_num);
339   m_register_locations[reg_num] = reg_loc;
340   return true;
341 }
342 
343 bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
344                                                 bool must_replace) {
345   if (must_replace &&
346       m_register_locations.find(reg_num) == m_register_locations.end())
347     return false;
348   RegisterLocation reg_loc;
349   reg_loc.SetSame();
350   m_register_locations[reg_num] = reg_loc;
351   return true;
352 }
353 
354 bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
355   return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&
356          m_afa_value == rhs.m_afa_value &&
357          m_unspecified_registers_are_undefined ==
358              rhs.m_unspecified_registers_are_undefined &&
359          m_register_locations == rhs.m_register_locations;
360 }
361 
362 void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
363   if (m_row_list.empty() ||
364       m_row_list.back()->GetOffset() != row_sp->GetOffset())
365     m_row_list.push_back(row_sp);
366   else
367     m_row_list.back() = row_sp;
368 }
369 
370 void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
371                            bool replace_existing) {
372   collection::iterator it = m_row_list.begin();
373   while (it != m_row_list.end()) {
374     RowSP row = *it;
375     if (row->GetOffset() >= row_sp->GetOffset())
376       break;
377     it++;
378   }
379   if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
380     m_row_list.insert(it, row_sp);
381   else if (replace_existing)
382     *it = row_sp;
383 }
384 
385 UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
386   RowSP row;
387   if (!m_row_list.empty()) {
388     if (offset == -1)
389       row = m_row_list.back();
390     else {
391       collection::const_iterator pos, end = m_row_list.end();
392       for (pos = m_row_list.begin(); pos != end; ++pos) {
393         if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
394           row = *pos;
395         else
396           break;
397       }
398     }
399   }
400   return row;
401 }
402 
403 bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
404   return idx < m_row_list.size();
405 }
406 
407 const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
408   if (idx < m_row_list.size())
409     return m_row_list[idx];
410   else {
411     Log *log = GetLog(LLDBLog::Unwind);
412     LLDB_LOGF(log,
413               "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
414               "(number rows is %u)",
415               idx, (uint32_t)m_row_list.size());
416     return UnwindPlan::RowSP();
417   }
418 }
419 
420 const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
421   if (m_row_list.empty()) {
422     Log *log = GetLog(LLDBLog::Unwind);
423     LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
424     return UnwindPlan::RowSP();
425   }
426   return m_row_list.back();
427 }
428 
429 int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
430 
431 void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
432   if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
433     m_plan_valid_address_range = range;
434 }
435 
436 bool UnwindPlan::PlanValidAtAddress(Address addr) {
437   // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
438   if (GetRowCount() == 0) {
439     Log *log = GetLog(LLDBLog::Unwind);
440     if (log) {
441       StreamString s;
442       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
443         LLDB_LOGF(log,
444                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
445                   "'%s' at address %s",
446                   m_source_name.GetCString(), s.GetData());
447       } else {
448         LLDB_LOGF(log,
449                   "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
450                   m_source_name.GetCString());
451       }
452     }
453     return false;
454   }
455 
456   // If the 0th Row of unwind instructions is missing, or if it doesn't provide
457   // a register to use to find the Canonical Frame Address, this is not a valid
458   // UnwindPlan.
459   if (GetRowAtIndex(0).get() == nullptr ||
460       GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
461           Row::FAValue::unspecified) {
462     Log *log = GetLog(LLDBLog::Unwind);
463     if (log) {
464       StreamString s;
465       if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
466         LLDB_LOGF(log,
467                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
468                   "for UnwindPlan '%s' at address %s",
469                   m_source_name.GetCString(), s.GetData());
470       } else {
471         LLDB_LOGF(log,
472                   "UnwindPlan is invalid -- no CFA register defined in row 0 "
473                   "for UnwindPlan '%s'",
474                   m_source_name.GetCString());
475       }
476     }
477     return false;
478   }
479 
480   if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
481       m_plan_valid_address_range.GetByteSize() == 0)
482     return true;
483 
484   if (!addr.IsValid())
485     return true;
486 
487   if (m_plan_valid_address_range.ContainsFileAddress(addr))
488     return true;
489 
490   return false;
491 }
492 
493 void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
494   if (!m_source_name.IsEmpty()) {
495     s.Printf("This UnwindPlan originally sourced from %s\n",
496              m_source_name.GetCString());
497   }
498   if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
499     TargetSP target_sp(thread->CalculateTarget());
500     addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
501     addr_t personality_func_load_addr =
502         m_personality_func_addr.GetLoadAddress(target_sp.get());
503 
504     if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
505         personality_func_load_addr != LLDB_INVALID_ADDRESS) {
506       s.Printf("LSDA address 0x%" PRIx64
507                ", personality routine is at address 0x%" PRIx64 "\n",
508                lsda_load_addr, personality_func_load_addr);
509     }
510   }
511   s.Printf("This UnwindPlan is sourced from the compiler: ");
512   switch (m_plan_is_sourced_from_compiler) {
513   case eLazyBoolYes:
514     s.Printf("yes.\n");
515     break;
516   case eLazyBoolNo:
517     s.Printf("no.\n");
518     break;
519   case eLazyBoolCalculate:
520     s.Printf("not specified.\n");
521     break;
522   }
523   s.Printf("This UnwindPlan is valid at all instruction locations: ");
524   switch (m_plan_is_valid_at_all_instruction_locations) {
525   case eLazyBoolYes:
526     s.Printf("yes.\n");
527     break;
528   case eLazyBoolNo:
529     s.Printf("no.\n");
530     break;
531   case eLazyBoolCalculate:
532     s.Printf("not specified.\n");
533     break;
534   }
535   s.Printf("This UnwindPlan is for a trap handler function: ");
536   switch (m_plan_is_for_signal_trap) {
537   case eLazyBoolYes:
538     s.Printf("yes.\n");
539     break;
540   case eLazyBoolNo:
541     s.Printf("no.\n");
542     break;
543   case eLazyBoolCalculate:
544     s.Printf("not specified.\n");
545     break;
546   }
547   if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
548       m_plan_valid_address_range.GetByteSize() > 0) {
549     s.PutCString("Address range of this UnwindPlan: ");
550     TargetSP target_sp(thread->CalculateTarget());
551     m_plan_valid_address_range.Dump(&s, target_sp.get(),
552                                     Address::DumpStyleSectionNameOffset);
553     s.EOL();
554   }
555   collection::const_iterator pos, begin = m_row_list.begin(),
556                                   end = m_row_list.end();
557   for (pos = begin; pos != end; ++pos) {
558     s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
559     (*pos)->Dump(s, this, thread, base_addr);
560     s.Printf("\n");
561   }
562 }
563 
564 void UnwindPlan::SetSourceName(const char *source) {
565   m_source_name = ConstString(source);
566 }
567 
568 ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
569 
570 const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
571                                                 uint32_t unwind_reg) const {
572   if (thread) {
573     RegisterContext *reg_ctx = thread->GetRegisterContext().get();
574     if (reg_ctx) {
575       uint32_t reg;
576       if (m_register_kind == eRegisterKindLLDB)
577         reg = unwind_reg;
578       else
579         reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
580                                                            unwind_reg);
581       if (reg != LLDB_INVALID_REGNUM)
582         return reg_ctx->GetRegisterInfoAtIndex(reg);
583     }
584   }
585   return nullptr;
586 }
587