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