mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 11:57:39 +08:00
The IT instruction can specify condition code for up to 4 consecutive instruction and it is used quite often by clang in epilogues causing an issue when trying to unwind from locations covered by the IT instruction and for locatins inmediately after the IT instruction. Changes made to fix it: * Introduce the concept of conditional instruction block what is a list of consecutive instructions with the same condition. We update the unwind information during the conditional instruction block and when we reach the end of it (first instruction with a differemt condition) then we restore the unwind information we had before the condition. * Fix a bug in the ARM instruction emulator where neither PC nor the ITSTATE was advanced when we reached an instruction what we can't decode. After the change we have no regression on android-arm running the regular test suit and TestStandardUnwind also passes when running it with clang as the compiler (previously it failed on an IT instruction). Differential revision: http://reviews.llvm.org/D16814 llvm-svn: 260368
565 lines
16 KiB
C++
565 lines
16 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/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, 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
|
|
{
|
|
// 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;
|
|
}
|
|
|