Fixed an ARM backtracing issue where if the previous frame was a thumb function and it was a tail call so that the current frame returned to an address that would fall into the next function, we would use the next function as the basis for how we unwound the previous frame's registers and of course get things wrong. We now fix the PC code address using the current ABI plug-in, and the ARM ABI plug-in has been modified to correctly fix the code address. So when we do the symbol context lookup, instead of taking an address like 0x1001 and decrementing 1, and looking up the symbol context for a frame, we now correctly fix 0x1001 to 0x1000, then decrement that by 1 to get the correct symbol context. I added a bunch more logging to "log enable lldb uwnind" to help us in the future. We now log the PC, FP and SP (if they are available), and we also dump the "active_row" that we find for unwinding a frame. llvm-svn: 147747
1463 lines
54 KiB
C++
1463 lines
54 KiB
C++
//===-- RegisterContextLLDB.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/lldb-private.h"
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/AddressRange.h"
|
|
#include "lldb/Core/DataBufferHeap.h"
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Core/RegisterValue.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Symbol/FuncUnwinders.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Symbol/Symbol.h"
|
|
#include "lldb/Expression/DWARFExpression.h"
|
|
#include "lldb/Target/ABI.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/DynamicLoader.h"
|
|
|
|
#include "RegisterContextLLDB.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
RegisterContextLLDB::RegisterContextLLDB
|
|
(
|
|
Thread& thread,
|
|
const SharedPtr &next_frame,
|
|
SymbolContext& sym_ctx,
|
|
uint32_t frame_number,
|
|
UnwindLLDB& unwind_lldb
|
|
) :
|
|
RegisterContext (thread, frame_number),
|
|
m_thread(thread),
|
|
m_fast_unwind_plan_sp (),
|
|
m_full_unwind_plan_sp (),
|
|
m_all_registers_available(false),
|
|
m_frame_type (-1),
|
|
m_cfa (LLDB_INVALID_ADDRESS),
|
|
m_start_pc (),
|
|
m_current_pc (),
|
|
m_current_offset (0),
|
|
m_current_offset_backed_up_one (0),
|
|
m_sym_ctx(sym_ctx),
|
|
m_sym_ctx_valid (false),
|
|
m_frame_number (frame_number),
|
|
m_registers(),
|
|
m_parent_unwind (unwind_lldb)
|
|
{
|
|
m_sym_ctx.Clear();
|
|
m_sym_ctx_valid = false;
|
|
|
|
if (IsFrameZero ())
|
|
{
|
|
InitializeZerothFrame ();
|
|
}
|
|
else
|
|
{
|
|
InitializeNonZerothFrame ();
|
|
}
|
|
|
|
// This same code exists over in the GetFullUnwindPlanForFrame() but it may not have been executed yet
|
|
if (IsFrameZero()
|
|
|| next_frame->m_frame_type == eSigtrampFrame
|
|
|| next_frame->m_frame_type == eDebuggerFrame)
|
|
{
|
|
m_all_registers_available = true;
|
|
}
|
|
}
|
|
|
|
// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
|
|
// executing frame.
|
|
|
|
void
|
|
RegisterContextLLDB::InitializeZerothFrame()
|
|
{
|
|
StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0));
|
|
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
|
|
if (m_thread.GetRegisterContext() == NULL)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
m_sym_ctx = frame_sp->GetSymbolContext (eSymbolContextFunction | eSymbolContextSymbol);
|
|
m_sym_ctx_valid = true;
|
|
AddressRange addr_range;
|
|
m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range);
|
|
|
|
m_current_pc = frame_sp->GetFrameCodeAddress();
|
|
|
|
static ConstString g_sigtramp_name ("_sigtramp");
|
|
if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == g_sigtramp_name) ||
|
|
(m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == g_sigtramp_name))
|
|
{
|
|
m_frame_type = eSigtrampFrame;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Detect eDebuggerFrame here.
|
|
m_frame_type = eNormalFrame;
|
|
}
|
|
|
|
// If we were able to find a symbol/function, set addr_range to the bounds of that symbol/function.
|
|
// else treat the current pc value as the start_pc and record no offset.
|
|
if (addr_range.GetBaseAddress().IsValid())
|
|
{
|
|
m_start_pc = addr_range.GetBaseAddress();
|
|
if (frame_sp->GetFrameCodeAddress().GetSection() == m_start_pc.GetSection())
|
|
{
|
|
m_current_offset = frame_sp->GetFrameCodeAddress().GetOffset() - m_start_pc.GetOffset();
|
|
}
|
|
else if (frame_sp->GetFrameCodeAddress().GetModule() == m_start_pc.GetModule())
|
|
{
|
|
// This means that whatever symbol we kicked up isn't really correct
|
|
// as no should cross section boundaries... We really should NULL out
|
|
// the function/symbol in this case unless there is a bad assumption
|
|
// here due to inlined functions?
|
|
m_current_offset = frame_sp->GetFrameCodeAddress().GetFileAddress() - m_start_pc.GetFileAddress();
|
|
}
|
|
m_current_offset_backed_up_one = m_current_offset;
|
|
}
|
|
else
|
|
{
|
|
m_start_pc = m_current_pc;
|
|
m_current_offset = -1;
|
|
m_current_offset_backed_up_one = -1;
|
|
}
|
|
|
|
// We've set m_frame_type and m_sym_ctx before these calls.
|
|
|
|
m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
|
|
|
|
const UnwindPlan::Row *active_row = NULL;
|
|
int cfa_offset = 0;
|
|
int row_register_kind;
|
|
if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
if (active_row && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(&m_thread.GetProcess().GetTarget()));
|
|
log->Printf("%*sFrame %u active row: %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
|
|
if (active_row == NULL)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
|
|
addr_t cfa_regval;
|
|
if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
cfa_offset = active_row->GetCFAOffset ();
|
|
|
|
m_cfa = cfa_regval + cfa_offset;
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u cfa_regval = 0x%16.16llx (cfa_regval = 0x%16.16llx, cfa_offset = %i)",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
m_cfa, cfa_regval, cfa_offset);
|
|
}
|
|
|
|
|
|
// A couple of sanity checks..
|
|
if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not find a valid cfa address",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sThread %d Frame %u initialized frame current pc is 0x%llx cfa is 0x%llx using %s UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "",
|
|
m_thread.GetIndexID(),
|
|
m_frame_number,
|
|
(uint64_t) m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()),
|
|
(uint64_t) m_cfa,
|
|
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
}
|
|
|
|
// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the RegisterContextLLDB "below" it
|
|
// to provide things like its current pc value.
|
|
|
|
void
|
|
RegisterContextLLDB::InitializeNonZerothFrame()
|
|
{
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
|
|
if (IsFrameZero ())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (!GetNextFrame().get() || !GetNextFrame()->IsValid())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
if (m_thread.GetRegisterContext() == NULL)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
addr_t pc;
|
|
if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not get pc value",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u pc = 0x%16.16llx",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, pc);
|
|
addr_t reg_val;
|
|
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
|
|
log->Printf("%*sFrame %u fp = 0x%16.16llx",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, reg_val);
|
|
|
|
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
|
|
log->Printf("%*sFrame %u sp = 0x%16.16llx",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, reg_val);
|
|
}
|
|
|
|
// A pc of 0x0 means it's the end of the stack crawl
|
|
if (pc == 0)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
// Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
|
|
// this will strip bit zero in case we read a PC from memory or from the LR.
|
|
ABI *abi = m_thread.GetProcess().GetABI().get();
|
|
if (abi)
|
|
pc = abi->FixCodeAddress(pc);
|
|
|
|
// Test the pc value to see if we know it's in an unmapped/non-executable region of memory.
|
|
uint32_t permissions;
|
|
if (m_thread.GetProcess().GetLoadAddressPermissions(pc, permissions)
|
|
&& (permissions & ePermissionsExecutable) == 0)
|
|
{
|
|
// If this is the second frame off the stack, we may have unwound the first frame
|
|
// incorrectly. But using the architecture default unwind plan may get us back on
|
|
// track -- albeit possibly skipping a real frame. Give this frame a clearly-invalid
|
|
// pc and see if we can get any further.
|
|
if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero())
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u had a pc of 0x%llx which is not in executable memory but on frame 1 -- allowing it once.",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, (uint64_t) pc);
|
|
}
|
|
m_frame_type = eSkipFrame;
|
|
}
|
|
else
|
|
{
|
|
// anywhere other than the second frame, a non-executable pc means we're off in the weeds -- stop now.
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, m_current_pc);
|
|
|
|
// If we don't have a Module for some reason, we're not going to find symbol/function information - just
|
|
// stick in some reasonable defaults and hope we can unwind past this frame.
|
|
if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL)
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u using architectural default unwind method",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
if (abi)
|
|
{
|
|
m_fast_unwind_plan_sp.reset ();
|
|
m_full_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
|
|
if (m_frame_type != eSkipFrame) // don't override eSkipFrame
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
}
|
|
m_all_registers_available = false;
|
|
m_current_offset = -1;
|
|
m_current_offset_backed_up_one = -1;
|
|
addr_t cfa_regval;
|
|
int row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
const UnwindPlan::Row *row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
|
|
if (row)
|
|
{
|
|
uint32_t cfa_regnum = row->GetCFARegister();
|
|
int cfa_offset = row->GetCFAOffset();
|
|
if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u failed to get cfa value",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
if (m_frame_type != eSkipFrame) // don't override eSkipFrame
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
}
|
|
return;
|
|
}
|
|
m_cfa = cfa_regval + cfa_offset;
|
|
|
|
// A couple of sanity checks..
|
|
if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not find a valid cfa address",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
// cfa_regval should point into the stack memory; if we can query memory region permissions,
|
|
// see if the memory is allocated & readable.
|
|
if (m_thread.GetProcess().GetLoadAddressPermissions(cfa_regval, permissions)
|
|
&& (permissions & ePermissionsReadable) == 0)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not find a row for function offset zero",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u initialized frame cfa is 0x%llx",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
(uint64_t) m_cfa);
|
|
}
|
|
return;
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
// We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us.
|
|
if ((m_current_pc.GetModule()->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
|
|
{
|
|
m_sym_ctx_valid = true;
|
|
}
|
|
|
|
AddressRange addr_range;
|
|
if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range))
|
|
{
|
|
m_sym_ctx_valid = false;
|
|
}
|
|
|
|
bool decr_pc_and_recompute_addr_range = false;
|
|
|
|
// If the symbol lookup failed...
|
|
if (m_sym_ctx_valid == false)
|
|
decr_pc_and_recompute_addr_range = true;
|
|
|
|
// Or if we're in the middle of the stack (and not "above" an asynchronous event like sigtramp),
|
|
// and our "current" pc is the start of a function...
|
|
if (m_sym_ctx_valid
|
|
&& GetNextFrame()->m_frame_type != eSigtrampFrame
|
|
&& GetNextFrame()->m_frame_type != eDebuggerFrame
|
|
&& addr_range.GetBaseAddress().IsValid()
|
|
&& addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection()
|
|
&& addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset())
|
|
{
|
|
decr_pc_and_recompute_addr_range = true;
|
|
}
|
|
|
|
// We need to back up the pc by 1 byte and re-search for the Symbol to handle the case where the "saved pc"
|
|
// value is pointing to the next function, e.g. if a function ends with a CALL instruction.
|
|
// FIXME this may need to be an architectural-dependent behavior; if so we'll need to add a member function
|
|
// to the ABI plugin and consult that.
|
|
if (decr_pc_and_recompute_addr_range)
|
|
{
|
|
Address temporary_pc(m_current_pc);
|
|
temporary_pc.SetOffset(m_current_pc.GetOffset() - 1);
|
|
m_sym_ctx.Clear();
|
|
m_sym_ctx_valid = false;
|
|
if ((m_current_pc.GetModule()->ResolveSymbolContextForAddress (temporary_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol)
|
|
{
|
|
m_sym_ctx_valid = true;
|
|
}
|
|
if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range))
|
|
{
|
|
m_sym_ctx_valid = false;
|
|
}
|
|
}
|
|
|
|
// If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function.
|
|
// else treat the current pc value as the start_pc and record no offset.
|
|
if (addr_range.GetBaseAddress().IsValid())
|
|
{
|
|
m_start_pc = addr_range.GetBaseAddress();
|
|
m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
|
|
m_current_offset_backed_up_one = m_current_offset;
|
|
if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0)
|
|
m_current_offset_backed_up_one--;
|
|
}
|
|
else
|
|
{
|
|
m_start_pc = m_current_pc;
|
|
m_current_offset = -1;
|
|
m_current_offset_backed_up_one = -1;
|
|
}
|
|
|
|
static ConstString sigtramp_name ("_sigtramp");
|
|
if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name)
|
|
|| (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name))
|
|
{
|
|
m_frame_type = eSigtrampFrame;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Detect eDebuggerFrame here.
|
|
if (m_frame_type != eSkipFrame) // don't override eSkipFrame
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
}
|
|
}
|
|
|
|
// We've set m_frame_type and m_sym_ctx before this call.
|
|
m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame ();
|
|
|
|
const UnwindPlan::Row *active_row = NULL;
|
|
int cfa_offset = 0;
|
|
int row_register_kind;
|
|
|
|
// Try to get by with just the fast UnwindPlan if possible - the full UnwindPlan may be expensive to get
|
|
// (e.g. if we have to parse the entire eh_frame section of an ObjectFile for the first time.)
|
|
|
|
if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind ();
|
|
if (active_row && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(&m_thread.GetProcess().GetTarget()));
|
|
log->Printf("%*sFrame %u active row: %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
|
|
if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
if (active_row && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(&m_thread.GetProcess().GetTarget()));
|
|
log->Printf("%*sFrame %u active row: %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (active_row == NULL)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
addr_t cfa_regval;
|
|
if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u failed to get cfa reg %d/%d",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
row_register_kind, active_row->GetCFARegister());
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
cfa_offset = active_row->GetCFAOffset ();
|
|
|
|
m_cfa = cfa_regval + cfa_offset;
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u cfa_regval = 0x%16.16llx (cfa_regval = 0x%16.16llx, cfa_offset = %i)",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
m_cfa, cfa_regval, cfa_offset);
|
|
}
|
|
|
|
// A couple of sanity checks..
|
|
if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1)
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not find a valid cfa address",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
// If we have a bad stack setup, we can get the same CFA value multiple times -- or even
|
|
// more devious, we can actually oscillate between two CFA values. Detect that here and
|
|
// break out to avoid a possible infinite loop in lldb trying to unwind the stack.
|
|
addr_t next_frame_cfa;
|
|
addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
|
|
if (GetNextFrame().get() && GetNextFrame()->GetCFA(next_frame_cfa))
|
|
{
|
|
bool repeating_frames = false;
|
|
if (next_frame_cfa == m_cfa)
|
|
{
|
|
repeating_frames = true;
|
|
}
|
|
else
|
|
{
|
|
if (GetNextFrame()->GetNextFrame() && GetNextFrame()->GetNextFrame()->GetCFA(next_next_frame_cfa)
|
|
&& next_next_frame_cfa == m_cfa)
|
|
{
|
|
repeating_frames = true;
|
|
}
|
|
}
|
|
if (repeating_frames)
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u same CFA address as next frame, assuming the unwind is looping - stopping",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u initialized frame current pc is 0x%llx cfa is 0x%llx",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
(uint64_t) m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()), (uint64_t) m_cfa);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
RegisterContextLLDB::IsFrameZero () const
|
|
{
|
|
return m_frame_number == 0;
|
|
}
|
|
|
|
|
|
// Find a fast unwind plan for this frame, if possible.
|
|
//
|
|
// On entry to this method,
|
|
//
|
|
// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct,
|
|
// 2. m_sym_ctx should already be filled in, and
|
|
// 3. m_current_pc should have the current pc value for this frame
|
|
// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown
|
|
|
|
UnwindPlanSP
|
|
RegisterContextLLDB::GetFastUnwindPlanForFrame ()
|
|
{
|
|
UnwindPlanSP unwind_plan_sp;
|
|
if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL || m_current_pc.GetModule()->GetObjectFile() == NULL)
|
|
return unwind_plan_sp;
|
|
|
|
if (IsFrameZero ())
|
|
return unwind_plan_sp;
|
|
|
|
FuncUnwindersSP func_unwinders_sp (m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx));
|
|
if (!func_unwinders_sp)
|
|
return unwind_plan_sp;
|
|
|
|
// If we're in _sigtramp(), unwinding past this frame requires special knowledge.
|
|
if (m_frame_type == eSigtrampFrame || m_frame_type == eDebuggerFrame)
|
|
return unwind_plan_sp;
|
|
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (m_thread);
|
|
if (unwind_plan_sp)
|
|
{
|
|
if (unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (log && log->GetVerbose())
|
|
{
|
|
const char *has_fast = "";
|
|
if (m_fast_unwind_plan_sp)
|
|
has_fast = ", and has a fast UnwindPlan";
|
|
log->Printf("%*sFrame %u frame has a fast UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
|
|
}
|
|
m_frame_type = eNormalFrame;
|
|
return unwind_plan_sp;
|
|
}
|
|
else
|
|
{
|
|
unwind_plan_sp.reset();
|
|
}
|
|
}
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
// On entry to this method,
|
|
//
|
|
// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct,
|
|
// 2. m_sym_ctx should already be filled in, and
|
|
// 3. m_current_pc should have the current pc value for this frame
|
|
// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown
|
|
|
|
UnwindPlanSP
|
|
RegisterContextLLDB::GetFullUnwindPlanForFrame ()
|
|
{
|
|
UnwindPlanSP unwind_plan_sp;
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
UnwindPlanSP arch_default_unwind_plan_sp;
|
|
|
|
ABI *abi = m_thread.GetProcess().GetABI().get();
|
|
if (abi)
|
|
{
|
|
arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
|
|
}
|
|
|
|
bool behaves_like_zeroth_frame = false;
|
|
if (IsFrameZero ()
|
|
|| GetNextFrame()->m_frame_type == eSigtrampFrame
|
|
|| GetNextFrame()->m_frame_type == eDebuggerFrame)
|
|
{
|
|
behaves_like_zeroth_frame = true;
|
|
// If this frame behaves like a 0th frame (currently executing or
|
|
// interrupted asynchronously), all registers can be retrieved.
|
|
m_all_registers_available = true;
|
|
}
|
|
|
|
// If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) so the pc is 0x0
|
|
// in the zeroth frame, we need to use the "unwind at first instruction" arch default UnwindPlan
|
|
// Also, if this Process can report on memory region attributes, any non-executable region means
|
|
// we jumped through a bad function pointer - handle the same way as 0x0.
|
|
|
|
if (behaves_like_zeroth_frame && m_current_pc.IsValid())
|
|
{
|
|
uint32_t permissions;
|
|
addr_t current_pc_addr = m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget());
|
|
if (current_pc_addr == 0
|
|
|| (m_thread.GetProcess().GetLoadAddressPermissions(current_pc_addr, permissions)
|
|
&& (permissions & ePermissionsExecutable) == 0))
|
|
{
|
|
unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
|
|
m_frame_type = eNormalFrame;
|
|
return unwind_plan_sp;
|
|
}
|
|
}
|
|
|
|
// No Module for the current pc, try using the architecture default unwind.
|
|
if (!m_current_pc.IsValid() || m_current_pc.GetModule() == NULL || m_current_pc.GetModule()->GetObjectFile() == NULL)
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
return arch_default_unwind_plan_sp;
|
|
}
|
|
|
|
FuncUnwindersSP func_unwinders_sp;
|
|
if (m_sym_ctx_valid)
|
|
{
|
|
func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
|
}
|
|
|
|
// No FuncUnwinders available for this pc, try using architectural default unwind.
|
|
if (!func_unwinders_sp)
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
return arch_default_unwind_plan_sp;
|
|
}
|
|
|
|
// If we're in _sigtramp(), unwinding past this frame requires special knowledge. On Mac OS X this knowledge
|
|
// is properly encoded in the eh_frame section, so prefer that if available.
|
|
// On other platforms we may need to provide a platform-specific UnwindPlan which encodes the details of
|
|
// how to unwind out of sigtramp.
|
|
if (m_frame_type == eSigtrampFrame)
|
|
{
|
|
m_fast_unwind_plan_sp.reset();
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
// Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame even when it's frame zero
|
|
// This comes up if we have hand-written functions in a Module and hand-written eh_frame. The assembly
|
|
// instruction inspection may fail and the eh_frame CFI were probably written with some care to do the
|
|
// right thing. It'd be nice if there was a way to ask the eh_frame directly if it is asynchronous
|
|
// (can be trusted at every instruction point) or synchronous (the normal case - only at call sites).
|
|
// But there is not.
|
|
if (m_thread.GetProcess().GetDynamicLoader()
|
|
&& m_thread.GetProcess().GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo (m_sym_ctx))
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
if (log && log->GetVerbose())
|
|
{
|
|
log->Printf("%*sFrame %u frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
return unwind_plan_sp;
|
|
}
|
|
}
|
|
|
|
// Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions
|
|
if (behaves_like_zeroth_frame)
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
if (log && log->GetVerbose())
|
|
{
|
|
log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
return unwind_plan_sp;
|
|
}
|
|
}
|
|
|
|
// Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
if (log && log->GetVerbose())
|
|
{
|
|
log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
// We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've
|
|
// struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible.
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
if (log && log->GetVerbose())
|
|
{
|
|
log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
// If nothing else, use the architectural default UnwindPlan and hope that does the job.
|
|
if (log && log->GetVerbose())
|
|
{
|
|
log->Printf("%*sFrame %u frame uses %s for full UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
arch_default_unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
return arch_default_unwind_plan_sp;
|
|
}
|
|
|
|
|
|
void
|
|
RegisterContextLLDB::InvalidateAllRegisters ()
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
}
|
|
|
|
size_t
|
|
RegisterContextLLDB::GetRegisterCount ()
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterCount();
|
|
}
|
|
|
|
const RegisterInfo *
|
|
RegisterContextLLDB::GetRegisterInfoAtIndex (uint32_t reg)
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg);
|
|
}
|
|
|
|
size_t
|
|
RegisterContextLLDB::GetRegisterSetCount ()
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterSetCount ();
|
|
}
|
|
|
|
const RegisterSet *
|
|
RegisterContextLLDB::GetRegisterSet (uint32_t reg_set)
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
|
|
}
|
|
|
|
uint32_t
|
|
RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
|
|
{
|
|
return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
|
|
const RegisterInfo *reg_info,
|
|
RegisterValue &value)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
bool success = false;
|
|
|
|
switch (regloc.type)
|
|
{
|
|
case UnwindLLDB::RegisterLocation::eRegisterInRegister:
|
|
{
|
|
const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
|
|
if (IsFrameZero ())
|
|
{
|
|
success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value);
|
|
}
|
|
else
|
|
{
|
|
success = GetNextFrame()->ReadRegister (other_reg_info, value);
|
|
}
|
|
}
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
|
|
success = value.SetUInt (regloc.location.inferred_value, reg_info->byte_size);
|
|
break;
|
|
|
|
case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
|
|
assert ("FIXME debugger inferior function call unwind");
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation:
|
|
{
|
|
Error error (ReadRegisterValueFromMemory(reg_info,
|
|
regloc.location.target_memory_location,
|
|
reg_info->byte_size,
|
|
value));
|
|
success = error.Success();
|
|
}
|
|
break;
|
|
default:
|
|
assert ("Unknown RegisterLocation type.");
|
|
break;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
|
|
const RegisterInfo *reg_info,
|
|
const RegisterValue &value)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
bool success = false;
|
|
|
|
switch (regloc.type)
|
|
{
|
|
case UnwindLLDB::RegisterLocation::eRegisterInRegister:
|
|
{
|
|
const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
|
|
if (IsFrameZero ())
|
|
{
|
|
success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value);
|
|
}
|
|
else
|
|
{
|
|
success = GetNextFrame()->WriteRegister (other_reg_info, value);
|
|
}
|
|
}
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterValueInferred:
|
|
case UnwindLLDB::RegisterLocation::eRegisterNotSaved:
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation:
|
|
assert ("FIXME debugger inferior function call unwind");
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation:
|
|
{
|
|
Error error (WriteRegisterValueToMemory (reg_info,
|
|
regloc.location.target_memory_location,
|
|
reg_info->byte_size,
|
|
value));
|
|
success = error.Success();
|
|
}
|
|
break;
|
|
default:
|
|
assert ("Unknown RegisterLocation type.");
|
|
break;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
|
|
bool
|
|
RegisterContextLLDB::IsValid () const
|
|
{
|
|
return m_frame_type != eNotAValidFrame;
|
|
}
|
|
|
|
// A skip frame is a bogus frame on the stack -- but one where we're likely to find a real frame farther
|
|
// up the stack if we keep looking. It's always the second frame in an unwind (i.e. the first frame after
|
|
// frame zero) where unwinding can be the trickiest. Ideally we'll mark up this frame in some way so the
|
|
// user knows we're displaying bad data and we may have skipped one frame of their real program in the
|
|
// process of getting back on track.
|
|
|
|
bool
|
|
RegisterContextLLDB::IsSkipFrame () const
|
|
{
|
|
return m_frame_type == eSkipFrame;
|
|
}
|
|
|
|
// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?
|
|
|
|
bool
|
|
RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, bool check_next_frame)
|
|
{
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
|
|
// Have we already found this register location?
|
|
if (!m_registers.empty())
|
|
{
|
|
std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>::const_iterator iterator;
|
|
iterator = m_registers.find (lldb_regnum);
|
|
if (iterator != m_registers.end())
|
|
{
|
|
regloc = iterator->second;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Are we looking for the CALLER's stack pointer? The stack pointer is defined to be the same as THIS frame's
|
|
// CFA so just return the CFA value. This is true on x86-32/x86-64 at least.
|
|
uint32_t sp_regnum;
|
|
if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum)
|
|
&& sp_regnum == lldb_regnum)
|
|
{
|
|
// make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value)
|
|
assert (sizeof (addr_t) <= sizeof (uint64_t));
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
|
|
regloc.location.inferred_value = m_cfa;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
|
|
// Look through the available UnwindPlans for the register location.
|
|
|
|
UnwindPlan::Row::RegisterLocation unwindplan_regloc;
|
|
bool have_unwindplan_regloc = false;
|
|
RegisterKind unwindplan_registerkind = (RegisterKind)-1;
|
|
|
|
if (m_fast_unwind_plan_sp)
|
|
{
|
|
const UnwindPlan::Row *active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind ();
|
|
uint32_t row_regnum;
|
|
if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum, (int) unwindplan_registerkind);
|
|
}
|
|
return false;
|
|
}
|
|
if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u supplying caller's saved reg %d's location using FastUnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
have_unwindplan_regloc = true;
|
|
}
|
|
}
|
|
|
|
if (!have_unwindplan_regloc)
|
|
{
|
|
// m_full_unwind_plan_sp being NULL means that we haven't tried to find a full UnwindPlan yet
|
|
if (!m_full_unwind_plan_sp)
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
|
|
|
|
if (m_full_unwind_plan_sp)
|
|
{
|
|
const UnwindPlan::Row *active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
uint32_t row_regnum;
|
|
if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum))
|
|
{
|
|
if (log)
|
|
{
|
|
if (unwindplan_registerkind == eRegisterKindGeneric)
|
|
log->Printf("%*sFrame %u could not convert lldb regnum %d into eRegisterKindGeneric reg numbering scheme",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
else
|
|
log->Printf("%*sFrame %u could not convert lldb regnum %d into %d RegisterKind reg numbering scheme",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum, (int) unwindplan_registerkind);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
|
|
{
|
|
have_unwindplan_regloc = true;
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u supplying caller's saved reg %d's location using %s UnwindPlan",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum, m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (have_unwindplan_regloc == false)
|
|
{
|
|
// If a volatile register is being requested, we don't want to forward the next frame's register contents
|
|
// up the stack -- the register is not retrievable at this frame.
|
|
ABI *abi = m_thread.GetProcess().GetABI().get();
|
|
if (abi)
|
|
{
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
|
|
if (reg_info && abi->RegisterIsVolatile (reg_info))
|
|
{
|
|
if (log && log->GetVerbose ())
|
|
{
|
|
log->Printf("%*sFrame %u did not supply reg location for %d because it is volatile",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (IsFrameZero ())
|
|
{
|
|
// This is frame 0 - we should return the actual live register context value
|
|
lldb_private::UnwindLLDB::RegisterLocation new_regloc;
|
|
new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
|
|
new_regloc.location.register_number = lldb_regnum;
|
|
m_registers[lldb_regnum] = new_regloc;
|
|
regloc = new_regloc;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (check_next_frame)
|
|
return m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1);
|
|
}
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not supply caller's reg %d location",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// unwindplan_regloc has valid contents about where to retrieve the register
|
|
if (unwindplan_regloc.IsUnspecified())
|
|
{
|
|
lldb_private::UnwindLLDB::RegisterLocation new_regloc;
|
|
new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved;
|
|
m_registers[lldb_regnum] = new_regloc;
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not supply caller's reg %d location",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsSame())
|
|
{
|
|
if (IsFrameZero ())
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not supply caller's reg %d location",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (check_next_frame)
|
|
return m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1);
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (unwindplan_regloc.IsCFAPlusOffset())
|
|
{
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
|
|
regloc.location.inferred_value = m_cfa + offset;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsAtCFAPlusOffset())
|
|
{
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
|
|
regloc.location.target_memory_location = m_cfa + offset;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsInOtherRegister())
|
|
{
|
|
uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
|
|
uint32_t row_regnum_in_lldb;
|
|
if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb))
|
|
{
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not supply caller's reg %d location",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
|
|
regloc.location.register_number = row_regnum_in_lldb;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression())
|
|
{
|
|
DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(),
|
|
unwindplan_regloc.GetDWARFExpressionLength(),
|
|
m_thread.GetProcess().GetByteOrder(), m_thread.GetProcess().GetAddressByteSize());
|
|
DWARFExpression dwarfexpr (dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength());
|
|
dwarfexpr.SetRegisterKind (unwindplan_registerkind);
|
|
ExecutionContext exe_ctx (&m_thread.GetProcess(), &m_thread, NULL);
|
|
Value result;
|
|
Error error;
|
|
if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, NULL, this, 0, NULL, result, &error))
|
|
{
|
|
addr_t val;
|
|
val = result.GetScalar().ULongLong();
|
|
if (unwindplan_regloc.IsDWARFExpression())
|
|
{
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
|
|
regloc.location.inferred_value = val;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
|
|
regloc.location.target_memory_location = val;
|
|
m_registers[lldb_regnum] = regloc;
|
|
return true;
|
|
}
|
|
}
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u tried to use IsDWARFExpression or IsAtDWARFExpression for reg %d but failed",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
log->Printf("%*sFrame %u could not supply caller's reg %d location",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
|
|
|
|
// assert ("UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported.");
|
|
return false;
|
|
}
|
|
|
|
// Retrieve a general purpose register value for THIS from, as saved by the NEXT frame, i.e. the frame that
|
|
// this frame called. e.g.
|
|
//
|
|
// foo () { }
|
|
// bar () { foo (); }
|
|
// main () { bar (); }
|
|
//
|
|
// stopped in foo() so
|
|
// frame 0 - foo
|
|
// frame 1 - bar
|
|
// frame 2 - main
|
|
// and this RegisterContext is for frame 1 (bar) - if we want to get the pc value for frame 1, we need to ask
|
|
// where frame 0 (the "next" frame) saved that and retrieve the value.
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadGPRValue (int register_kind, uint32_t regnum, addr_t &value)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
uint32_t lldb_regnum;
|
|
if (register_kind == eRegisterKindLLDB)
|
|
{
|
|
lldb_regnum = regnum;
|
|
}
|
|
else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
|
|
RegisterValue reg_value;
|
|
// if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers
|
|
if (IsFrameZero ())
|
|
{
|
|
if (m_thread.GetRegisterContext()->ReadRegister (reg_info, reg_value))
|
|
{
|
|
value = reg_value.GetAsUInt64();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
lldb_private::UnwindLLDB::RegisterLocation regloc;
|
|
if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1))
|
|
{
|
|
return false;
|
|
}
|
|
if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
|
|
{
|
|
value = reg_value.GetAsUInt64();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Find the value of a register in THIS frame
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
|
|
{
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
|
|
if (log && log->GetVerbose ())
|
|
{
|
|
log->Printf("%*sFrame %u looking for register saved location for reg %d",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
|
|
// If this is the 0th frame, hand this over to the live register context
|
|
if (IsFrameZero ())
|
|
{
|
|
if (log && log->GetVerbose ())
|
|
{
|
|
log->Printf("%*sFrame %u passing along to the live register context for reg %d",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return m_thread.GetRegisterContext()->ReadRegister (reg_info, value);
|
|
}
|
|
|
|
lldb_private::UnwindLLDB::RegisterLocation regloc;
|
|
// Find out where the NEXT frame saved THIS frame's register contents
|
|
if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1))
|
|
return false;
|
|
|
|
return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value);
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value)
|
|
{
|
|
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
|
|
if (log && log->GetVerbose ())
|
|
{
|
|
log->Printf("%*sFrame %u looking for register saved location for reg %d",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
|
|
// If this is the 0th frame, hand this over to the live register context
|
|
if (IsFrameZero ())
|
|
{
|
|
if (log && log->GetVerbose ())
|
|
{
|
|
log->Printf("%*sFrame %u passing along to the live register context for reg %d",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number,
|
|
lldb_regnum);
|
|
}
|
|
return m_thread.GetRegisterContext()->WriteRegister (reg_info, value);
|
|
}
|
|
|
|
lldb_private::UnwindLLDB::RegisterLocation regloc;
|
|
// Find out where the NEXT frame saved THIS frame's register contents
|
|
if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1))
|
|
return false;
|
|
|
|
return WriteRegisterValueToRegisterLocation (regloc, reg_info, value);
|
|
}
|
|
|
|
// Don't need to implement this one
|
|
bool
|
|
RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Don't need to implement this one
|
|
bool
|
|
RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Retrieve the pc value for THIS from
|
|
|
|
bool
|
|
RegisterContextLLDB::GetCFA (addr_t& cfa)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
if (m_cfa == LLDB_INVALID_ADDRESS)
|
|
{
|
|
return false;
|
|
}
|
|
cfa = m_cfa;
|
|
return true;
|
|
}
|
|
|
|
|
|
RegisterContextLLDB::SharedPtr
|
|
RegisterContextLLDB::GetNextFrame () const
|
|
{
|
|
RegisterContextLLDB::SharedPtr regctx;
|
|
if (m_frame_number == 0)
|
|
return regctx;
|
|
return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number - 1);
|
|
}
|
|
|
|
RegisterContextLLDB::SharedPtr
|
|
RegisterContextLLDB::GetPrevFrame () const
|
|
{
|
|
RegisterContextLLDB::SharedPtr regctx;
|
|
return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number + 1);
|
|
}
|
|
|
|
// Retrieve the address of the start of the function of THIS frame
|
|
|
|
bool
|
|
RegisterContextLLDB::GetStartPC (addr_t& start_pc)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
if (!m_start_pc.IsValid())
|
|
{
|
|
return ReadPC (start_pc);
|
|
}
|
|
start_pc = m_start_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget());
|
|
return true;
|
|
}
|
|
|
|
// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadPC (addr_t& pc)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
|
|
{
|
|
// A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk.
|
|
// On the currently executing frame (or such a frame interrupted asynchronously by sigtramp et al) this may
|
|
// occur if code has jumped through a NULL pointer -- we want to be able to unwind past that frame to help
|
|
// find the bug.
|
|
|
|
if (m_all_registers_available == false
|
|
&& (pc == 0 || pc == 1))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|