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
22using namespace lldb;
23using namespace lldb_private;
24
25bool UnwindPlan::Row::RegisterLocation::
26operator==(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.
56void 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.
65void 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
72static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
73GetByteOrderAndAddrSize(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
83static 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
93void 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
159static 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
168bool UnwindPlan::Row::FAValue::
169operator==(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
192void 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
217void 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
225void 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
249UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
250
251bool 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
266void 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
273void 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
279bool 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
291bool 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
303bool 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
320bool 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
331bool 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
343bool 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
354bool 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
362void 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
370void 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
385UnwindPlan::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
403bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
404  return idx < m_row_list.size();
405}
406
407const 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
420const 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
429int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
430
431void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
432  if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
433    m_plan_valid_address_range = range;
434}
435
436bool 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
493void 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
564void UnwindPlan::SetSourceName(const char *source) {
565  m_source_name = ConstString(source);
566}
567
568ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
569
570const 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