text: //===-- UnwindPlan.cpp ----------------------------------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "lldb/Symbol/UnwindPlan.h"
  
  #include "lldb/Expression/DWARFExpression.h"
  #include "lldb/Target/Process.h"
  #include "lldb/Target/RegisterContext.h"
  #include "lldb/Target/Target.h"
  #include "lldb/Target/Thread.h"
  #include "lldb/Utility/ConstString.h"
  #include "lldb/Utility/Log.h"
  #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
  
  using namespace lldb;
  using namespace lldb_private;
  
  bool UnwindPlan::Row::RegisterLocation::
  operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
    if (m_type == rhs.m_type) {
      switch (m_type) {
      case unspecified:
      case undefined:
      case same:
        return true;
  
      case atCFAPlusOffset:
      case isCFAPlusOffset:
      case atAFAPlusOffset:
      case isAFAPlusOffset:
        return m_location.offset == rhs.m_location.offset;
  
      case inOtherRegister:
        return m_location.reg_num == rhs.m_location.reg_num;
  
      case atDWARFExpression:
      case isDWARFExpression:
        if (m_location.expr.length == rhs.m_location.expr.length)
          return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
                         m_location.expr.length);
        break;
      }
    }
    return false;
  }
  
  // This function doesn't copy the dwarf expression bytes; they must remain in
  // allocated memory for the lifespan of this UnwindPlan object.
  void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
      const uint8_t *opcodes, uint32_t len) {
    m_type = atDWARFExpression;
    m_location.expr.opcodes = opcodes;
    m_location.expr.length = len;
  }
  
  // This function doesn't copy the dwarf expression bytes; they must remain in
  // allocated memory for the lifespan of this UnwindPlan object.
  void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
      const uint8_t *opcodes, uint32_t len) {
    m_type = isDWARFExpression;
    m_location.expr.opcodes = opcodes;
    m_location.expr.length = len;
  }
  
  static llvm::Optional<std::pair<lldb::ByteOrder, uint32_t>>
  GetByteOrderAndAddrSize(Thread *thread) {
    if (!thread)
      return llvm::None;
    ProcessSP process_sp = thread->GetProcess();
    if (!process_sp)
      return llvm::None;
    ArchSpec arch = process_sp->GetTarget().GetArchitecture();
    return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
  }
  
  static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
    if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
      llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,
                               order_and_width->second);
      llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32)
          .print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr, nullptr);
    } else
      s.PutCString("dwarf-expr");
  }
  
  void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
                                               const UnwindPlan *unwind_plan,
                                               const UnwindPlan::Row *row,
                                               Thread *thread,
                                               bool verbose) const {
    switch (m_type) {
    case unspecified:
      if (verbose)
        s.PutCString("=<unspec>");
      else
        s.PutCString("=!");
      break;
    case undefined:
      if (verbose)
        s.PutCString("=<undef>");
      else
        s.PutCString("=?");
      break;
    case same:
      s.PutCString("= <same>");
      break;
  
    case atCFAPlusOffset:
    case isCFAPlusOffset: {
      s.PutChar('=');
      if (m_type == atCFAPlusOffset)
        s.PutChar('[');
      s.Printf("CFA%+d", m_location.offset);
      if (m_type == atCFAPlusOffset)
        s.PutChar(']');
    } break;
  
    case atAFAPlusOffset:
    case isAFAPlusOffset: {
      s.PutChar('=');
      if (m_type == atAFAPlusOffset)
        s.PutChar('[');
      s.Printf("AFA%+d", m_location.offset);
      if (m_type == atAFAPlusOffset)
        s.PutChar(']');
    } break;
  
    case inOtherRegister: {
      const RegisterInfo *other_reg_info = nullptr;
      if (unwind_plan)
        other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
      if (other_reg_info)
        s.Printf("=%s", other_reg_info->name);
      else
        s.Printf("=reg(%u)", m_location.reg_num);
    } break;
  
    case atDWARFExpression:
    case isDWARFExpression: {
      s.PutChar('=');
      if (m_type == atDWARFExpression)
        s.PutChar('[');
      DumpDWARFExpr(
          s, llvm::makeArrayRef(m_location.expr.opcodes, m_location.expr.length),
          thread);
      if (m_type == atDWARFExpression)
        s.PutChar(']');
    } break;
    }
  }
  
  static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
                               Thread *thread, uint32_t reg_num) {
    const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
    if (reg_info)
      s.PutCString(reg_info->name);
    else
      s.Printf("reg(%u)", reg_num);
  }
  
  bool UnwindPlan::Row::FAValue::
  operator==(const UnwindPlan::Row::FAValue &rhs) const {
    if (m_type == rhs.m_type) {
      switch (m_type) {
      case unspecified:
      case isRaSearch:
        return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
  
      case isRegisterPlusOffset:
        return m_value.reg.offset == rhs.m_value.reg.offset;
  
      case isRegisterDereferenced:
        return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
  
      case isDWARFExpression:
        if (m_value.expr.length == rhs.m_value.expr.length)
          return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
                         m_value.expr.length);
        break;
      }
    }
    return false;
  }
  
  void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
                                       Thread *thread) const {
    switch (m_type) {
    case isRegisterPlusOffset:
      DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
      s.Printf("%+3d", m_value.reg.offset);
      break;
    case isRegisterDereferenced:
      s.PutChar('[');
      DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
      s.PutChar(']');
      break;
    case isDWARFExpression:
      DumpDWARFExpr(s,
                    llvm::makeArrayRef(m_value.expr.opcodes, m_value.expr.length),
                    thread);
      break;
    case unspecified:
      s.PutCString("unspecified");
      break;
    case isRaSearch:
      s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
      break;
    }
  }
  
  void UnwindPlan::Row::Clear() {
    m_cfa_value.SetUnspecified();
    m_afa_value.SetUnspecified();
    m_offset = 0;
    m_register_locations.clear();
  }
  
  void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
                             Thread *thread, addr_t base_addr) const {
    if (base_addr != LLDB_INVALID_ADDRESS)
      s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
    else
      s.Printf("%4" PRId64 ": CFA=", GetOffset());
  
    m_cfa_value.Dump(s, unwind_plan, thread);
  
    if (!m_afa_value.IsUnspecified()) {
      s.Printf(" AFA=");
      m_afa_value.Dump(s, unwind_plan, thread);
    }
  
    s.Printf(" => ");
    for (collection::const_iterator idx = m_register_locations.begin();
         idx != m_register_locations.end(); ++idx) {
      DumpRegisterName(s, unwind_plan, thread, idx->first);
      const bool verbose = false;
      idx->second.Dump(s, unwind_plan, this, thread, verbose);
      s.PutChar(' ');
    }
    s.EOL();
  }
  
  UnwindPlan::Row::Row()
      : m_offset(0), m_cfa_value(), m_afa_value(), m_register_locations() {}
  
  bool UnwindPlan::Row::GetRegisterInfo(
      uint32_t reg_num,
      UnwindPlan::Row::RegisterLocation &register_location) const {
    collection::const_iterator pos = m_register_locations.find(reg_num);
    if (pos != m_register_locations.end()) {
      register_location = pos->second;
      return true;
    }
    return false;
  }
  
  void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
    collection::const_iterator pos = m_register_locations.find(reg_num);
    if (pos != m_register_locations.end()) {
      m_register_locations.erase(pos);
    }
  }
  
  void UnwindPlan::Row::SetRegisterInfo(
      uint32_t reg_num,
      const UnwindPlan::Row::RegisterLocation register_location) {
    m_register_locations[reg_num] = register_location;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
                                                             int32_t offset,
                                                             bool can_replace) {
    if (!can_replace &&
        m_register_locations.find(reg_num) != m_register_locations.end())
      return false;
    RegisterLocation reg_loc;
    reg_loc.SetAtCFAPlusOffset(offset);
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
                                                             int32_t offset,
                                                             bool can_replace) {
    if (!can_replace &&
        m_register_locations.find(reg_num) != m_register_locations.end())
      return false;
    RegisterLocation reg_loc;
    reg_loc.SetIsCFAPlusOffset(offset);
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToUndefined(
      uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
    collection::iterator pos = m_register_locations.find(reg_num);
    collection::iterator end = m_register_locations.end();
  
    if (pos != end) {
      if (!can_replace)
        return false;
      if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
        return false;
    }
    RegisterLocation reg_loc;
    reg_loc.SetUndefined();
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
                                                         bool can_replace) {
    if (!can_replace &&
        m_register_locations.find(reg_num) != m_register_locations.end())
      return false;
    RegisterLocation reg_loc;
    reg_loc.SetUnspecified();
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
                                                      uint32_t other_reg_num,
                                                      bool can_replace) {
    if (!can_replace &&
        m_register_locations.find(reg_num) != m_register_locations.end())
      return false;
    RegisterLocation reg_loc;
    reg_loc.SetInRegister(other_reg_num);
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
                                                  bool must_replace) {
    if (must_replace &&
        m_register_locations.find(reg_num) == m_register_locations.end())
      return false;
    RegisterLocation reg_loc;
    reg_loc.SetSame();
    m_register_locations[reg_num] = reg_loc;
    return true;
  }
  
  bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
    return m_offset == rhs.m_offset &&
        m_cfa_value == rhs.m_cfa_value &&
        m_afa_value == rhs.m_afa_value &&
        m_register_locations == rhs.m_register_locations;
  }
  
  void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
    if (m_row_list.empty() ||
        m_row_list.back()->GetOffset() != row_sp->GetOffset())
      m_row_list.push_back(row_sp);
    else
      m_row_list.back() = row_sp;
  }
  
  void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
                             bool replace_existing) {
    collection::iterator it = m_row_list.begin();
    while (it != m_row_list.end()) {
      RowSP row = *it;
      if (row->GetOffset() >= row_sp->GetOffset())
        break;
      it++;
    }
    if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset())
      m_row_list.insert(it, row_sp);
    else if (replace_existing)
      *it = row_sp;
  }
  
  UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
    RowSP row;
    if (!m_row_list.empty()) {
      if (offset == -1)
        row = m_row_list.back();
      else {
        collection::const_iterator pos, end = m_row_list.end();
        for (pos = m_row_list.begin(); pos != end; ++pos) {
          if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
            row = *pos;
          else
            break;
        }
      }
    }
    return row;
  }
  
  bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
    return idx < m_row_list.size();
  }
  
  const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
    if (idx < m_row_list.size())
      return m_row_list[idx];
    else {
      Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
      LLDB_LOGF(log,
                "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
                "(number rows is %u)",
                idx, (uint32_t)m_row_list.size());
      return UnwindPlan::RowSP();
    }
  }
  
  const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
    if (m_row_list.empty()) {
      Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
      LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
      return UnwindPlan::RowSP();
    }
    return m_row_list.back();
  }
  
  int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
  
  void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
    if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
      m_plan_valid_address_range = range;
  }
  
  bool UnwindPlan::PlanValidAtAddress(Address addr) {
    // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
    if (GetRowCount() == 0) {
      Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
      if (log) {
        StreamString s;
        if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
          LLDB_LOGF(log,
                    "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
                    "'%s' at address %s",
                    m_source_name.GetCString(), s.GetData());
        } else {
          LLDB_LOGF(log,
                    "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
                    m_source_name.GetCString());
        }
      }
      return false;
    }
  
    // If the 0th Row of unwind instructions is missing, or if it doesn't provide
    // a register to use to find the Canonical Frame Address, this is not a valid
    // UnwindPlan.
    if (GetRowAtIndex(0).get() == nullptr ||
        GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
            Row::FAValue::unspecified) {
      Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
      if (log) {
        StreamString s;
        if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
          LLDB_LOGF(log,
                    "UnwindPlan is invalid -- no CFA register defined in row 0 "
                    "for UnwindPlan '%s' at address %s",
                    m_source_name.GetCString(), s.GetData());
        } else {
          LLDB_LOGF(log,
                    "UnwindPlan is invalid -- no CFA register defined in row 0 "
                    "for UnwindPlan '%s'",
                    m_source_name.GetCString());
        }
      }
      return false;
    }
  
    if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
        m_plan_valid_address_range.GetByteSize() == 0)
      return true;
  
    if (!addr.IsValid())
      return true;
  
    if (m_plan_valid_address_range.ContainsFileAddress(addr))
      return true;
  
    return false;
  }
  
  void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
    if (!m_source_name.IsEmpty()) {
      s.Printf("This UnwindPlan originally sourced from %s\n",
               m_source_name.GetCString());
    }
    if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) {
      TargetSP target_sp(thread->CalculateTarget());
      addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
      addr_t personality_func_load_addr =
          m_personality_func_addr.GetLoadAddress(target_sp.get());
  
      if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
          personality_func_load_addr != LLDB_INVALID_ADDRESS) {
        s.Printf("LSDA address 0x%" PRIx64
                 ", personality routine is at address 0x%" PRIx64 "\n",
                 lsda_load_addr, personality_func_load_addr);
      }
    }
    s.Printf("This UnwindPlan is sourced from the compiler: ");
    switch (m_plan_is_sourced_from_compiler) {
    case eLazyBoolYes:
      s.Printf("yes.\n");
      break;
    case eLazyBoolNo:
      s.Printf("no.\n");
      break;
    case eLazyBoolCalculate:
      s.Printf("not specified.\n");
      break;
    }
    s.Printf("This UnwindPlan is valid at all instruction locations: ");
    switch (m_plan_is_valid_at_all_instruction_locations) {
    case eLazyBoolYes:
      s.Printf("yes.\n");
      break;
    case eLazyBoolNo:
      s.Printf("no.\n");
      break;
    case eLazyBoolCalculate:
      s.Printf("not specified.\n");
      break;
    }
    s.Printf("This UnwindPlan is for a trap handler function: ");
    switch (m_plan_is_for_signal_trap) {
    case eLazyBoolYes:
      s.Printf("yes.\n");
      break;
    case eLazyBoolNo:
      s.Printf("no.\n");
      break;
    case eLazyBoolCalculate:
      s.Printf("not specified.\n");
      break;
    }
    if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
        m_plan_valid_address_range.GetByteSize() > 0) {
      s.PutCString("Address range of this UnwindPlan: ");
      TargetSP target_sp(thread->CalculateTarget());
      m_plan_valid_address_range.Dump(&s, target_sp.get(),
                                      Address::DumpStyleSectionNameOffset);
      s.EOL();
    }
    collection::const_iterator pos, begin = m_row_list.begin(),
                                    end = m_row_list.end();
    for (pos = begin; pos != end; ++pos) {
      s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
      (*pos)->Dump(s, this, thread, base_addr);
    }
  }
  
  void UnwindPlan::SetSourceName(const char *source) {
    m_source_name = ConstString(source);
  }
  
  ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
  
  const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
                                                  uint32_t unwind_reg) const {
    if (thread) {
      RegisterContext *reg_ctx = thread->GetRegisterContext().get();
      if (reg_ctx) {
        uint32_t reg;
        if (m_register_kind == eRegisterKindLLDB)
          reg = unwind_reg;
        else
          reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
                                                             unwind_reg);
        if (reg != LLDB_INVALID_REGNUM)
          return reg_ctx->GetRegisterInfoAtIndex(reg);
      }
    }
    return nullptr;
  }
  