mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 21:53:12 +08:00
respective ABI plugins as they were plug-ins that supplied ABI specfic info. Also hookep up the UnwindAssemblyInstEmulation so that it can generate the unwind plans for ARM. Changed the way ABI plug-ins are handed out when you get an instance from the plug-in manager. They used to return pointers that would be mananged individually by each client that requested them, but now they are handed out as shared pointers since there is no state in the ABI objects, they can be shared. llvm-svn: 131193
448 lines
13 KiB
C++
448 lines
13 KiB
C++
//===-- 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/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('[');
|
|
if (verbose)
|
|
s.Printf ("CFA%+d", m_location.offset);
|
|
|
|
if (unwind_plan && row)
|
|
{
|
|
const uint32_t cfa_reg = row->GetCFARegister();
|
|
const RegisterInfo *cfa_reg_info = unwind_plan->GetRegisterInfo (thread, cfa_reg);
|
|
const int32_t offset = row->GetCFAOffset() + m_location.offset;
|
|
if (verbose)
|
|
{
|
|
if (cfa_reg_info)
|
|
s.Printf (" (%s%+d)", cfa_reg_info->name, offset);
|
|
else
|
|
s.Printf (" (reg(%u)%+d)", cfa_reg_info->name, offset);
|
|
}
|
|
else
|
|
{
|
|
if (cfa_reg_info)
|
|
s.Printf ("%s", cfa_reg_info->name);
|
|
else
|
|
s.Printf ("reg(%u)", cfa_reg_info->name);
|
|
if (offset != 0)
|
|
s.Printf ("%+d", offset);
|
|
}
|
|
}
|
|
if (m_type == atCFAPlusOffset)
|
|
s.PutChar(']');
|
|
}
|
|
break;
|
|
|
|
case inOtherRegister:
|
|
{
|
|
const RegisterInfo *other_reg_info = NULL;
|
|
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;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
UnwindPlan::Row::Clear ()
|
|
{
|
|
m_offset = 0;
|
|
m_cfa_reg_num = 0;
|
|
m_cfa_offset = 0;
|
|
m_register_locations.clear();
|
|
}
|
|
|
|
void
|
|
UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, addr_t base_addr) const
|
|
{
|
|
const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo (thread, GetCFARegister());
|
|
|
|
if (base_addr != LLDB_INVALID_ADDRESS)
|
|
s.Printf ("0x%16.16llx: CFA=", base_addr + GetOffset());
|
|
else
|
|
s.Printf ("0x%8.8x: CFA=", GetOffset());
|
|
|
|
if (reg_info)
|
|
s.Printf ("%s", reg_info->name);
|
|
else
|
|
s.Printf ("reg(%u)", GetCFARegister());
|
|
s.Printf ("%+3d =>", GetCFAOffset ());
|
|
for (collection::const_iterator idx = m_register_locations.begin (); idx != m_register_locations.end (); ++idx)
|
|
{
|
|
reg_info = unwind_plan->GetRegisterInfo (thread, idx->first);
|
|
if (reg_info)
|
|
s.Printf ("%s", reg_info->name);
|
|
else
|
|
s.Printf ("reg(%u)", 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_reg_num(0),
|
|
m_cfa_offset(0),
|
|
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::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;
|
|
}
|
|
|
|
void
|
|
UnwindPlan::Row::SetCFARegister (uint32_t reg_num)
|
|
{
|
|
m_cfa_reg_num = reg_num;
|
|
}
|
|
|
|
void
|
|
UnwindPlan::AppendRow (const UnwindPlan::Row &row)
|
|
{
|
|
if (m_row_list.empty() || m_row_list.back().GetOffset() != row.GetOffset())
|
|
m_row_list.push_back(row);
|
|
else
|
|
m_row_list.back() = row;
|
|
}
|
|
|
|
const UnwindPlan::Row *
|
|
UnwindPlan::GetRowForFunctionOffset (int offset) const
|
|
{
|
|
const UnwindPlan::Row *row_ptr = NULL;
|
|
if (!m_row_list.empty())
|
|
{
|
|
if (offset == -1)
|
|
row_ptr = &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() <= offset)
|
|
row_ptr = &*pos;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return row_ptr;
|
|
}
|
|
|
|
bool
|
|
UnwindPlan::IsValidRowIndex (uint32_t idx) const
|
|
{
|
|
return idx < m_row_list.size();
|
|
}
|
|
|
|
const UnwindPlan::Row&
|
|
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::Row&
|
|
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 (!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_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0)
|
|
{
|
|
s.PutCString ("Address range of this UnwindPlan: ");
|
|
m_plan_valid_address_range.Dump (&s, &thread->GetProcess().GetTarget(), Address::DumpStyleSectionNameOffset);
|
|
s.EOL();
|
|
}
|
|
else
|
|
{
|
|
s.PutCString ("No valid address range recorded for this UnwindPlan.\n");
|
|
}
|
|
s.Printf ("UnwindPlan register kind %d", m_register_kind);
|
|
switch (m_register_kind)
|
|
{
|
|
case eRegisterKindGCC: s.PutCString (" [eRegisterKindGCC]"); break;
|
|
case eRegisterKindDWARF: s.PutCString (" [eRegisterKindDWARF]"); break;
|
|
case eRegisterKindGeneric: s.PutCString (" [eRegisterKindGeneric]"); break;
|
|
case eRegisterKindGDB: s.PutCString (" [eRegisterKindGDB]"); break;
|
|
case eRegisterKindLLDB: s.PutCString (" [eRegisterKindLLDB]"); break;
|
|
default: s.PutCString (" [eRegisterKind???]"); break;
|
|
}
|
|
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 NULL;
|
|
}
|
|
|