| //===-- 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& 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_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) |
| { |
| collection::iterator it = m_row_list.begin(); |
| while (it != m_row_list.end()) { |
| RowSP row = *it; |
| if (row->GetOffset() > row_sp->GetOffset()) |
| break; |
| it++; |
| } |
| m_row_list.insert(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 |
| { |
| // You must call IsValidRowIndex(idx) first before calling this!!! |
| assert (idx < m_row_list.size()); |
| return m_row_list[idx]; |
| } |
| |
| const UnwindPlan::RowSP |
| UnwindPlan::GetLastRow () const |
| { |
| // You must call GetRowCount() first to make sure there is at least one row |
| assert (!m_row_list.empty()); |
| 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; |
| } |
| |