|  | //===-- UnwindPlan.cpp ----------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "lldb/Symbol/UnwindPlan.h" | 
|  |  | 
|  | #include "lldb/Core/ConstString.h" | 
|  | #include "lldb/Core/Log.h" | 
|  | #include "lldb/Target/Process.h" | 
|  | #include "lldb/Target/RegisterContext.h" | 
|  | #include "lldb/Target/Thread.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: | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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 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.PutCString("[dwarf-expr]"); | 
|  | else | 
|  | s.PutCString("dwarf-expr"); | 
|  | } 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::CFAValue:: | 
|  | operator==(const UnwindPlan::Row::CFAValue &rhs) const { | 
|  | if (m_type == rhs.m_type) { | 
|  | switch (m_type) { | 
|  | case unspecified: | 
|  | return true; | 
|  |  | 
|  | 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::CFAValue::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: | 
|  | s.PutCString("dwarf-expr"); | 
|  | break; | 
|  | default: | 
|  | s.PutCString("unspecified"); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void UnwindPlan::Row::Clear() { | 
|  | m_cfa_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); | 
|  | 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_register_locations() {} | 
|  |  | 
|  | bool UnwindPlan::Row::GetRegisterInfo( | 
|  | uint32_t reg_num, | 
|  | UnwindPlan::Row::RegisterLocation ®ister_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_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)); | 
|  | if (log) | 
|  | log->Printf("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)); | 
|  | if (log) | 
|  | log->Printf("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)) { | 
|  | log->Printf("UnwindPlan is invalid -- no unwind rows for UnwindPlan " | 
|  | "'%s' at address %s", | 
|  | m_source_name.GetCString(), s.GetData()); | 
|  | } else { | 
|  | log->Printf( | 
|  | "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::CFAValue::unspecified) { | 
|  | Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); | 
|  | if (log) { | 
|  | StreamString s; | 
|  | if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) { | 
|  | log->Printf("UnwindPlan is invalid -- no CFA register defined in row 0 " | 
|  | "for UnwindPlan '%s' at address %s", | 
|  | m_source_name.GetCString(), s.GetData()); | 
|  | } else { | 
|  | log->Printf("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; | 
|  | } | 
|  | 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; | 
|  | } |