2133 lines
85 KiB
C++
2133 lines
85 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/Module.h"
|
|
#include "lldb/Core/RegisterValue.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Expression/DWARFExpression.h"
|
|
#include "lldb/Symbol/ArmUnwindInfo.h"
|
|
#include "lldb/Symbol/DWARFCallFrameInfo.h"
|
|
#include "lldb/Symbol/FuncUnwinders.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/Symbol.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Target/ABI.h"
|
|
#include "lldb/Target/DynamicLoader.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Platform.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/SectionLoadList.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
#include "RegisterContextLLDB.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx)
|
|
{
|
|
if (sym_ctx.symbol)
|
|
return sym_ctx.symbol->GetName();
|
|
else if (sym_ctx.function)
|
|
return sym_ctx.function->GetName();
|
|
return ConstString();
|
|
}
|
|
|
|
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_fallback_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(false);
|
|
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 == eTrapHandlerFrame
|
|
|| next_frame->m_frame_type == eDebuggerFrame)
|
|
{
|
|
m_all_registers_available = true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::IsUnwindPlanValidForCurrentPC(lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset)
|
|
{
|
|
if (!unwind_plan_sp)
|
|
return false;
|
|
|
|
// check if m_current_pc is valid
|
|
if (unwind_plan_sp->PlanValidAtAddress(m_current_pc))
|
|
{
|
|
// yes - current offset can be used as is
|
|
valid_pc_offset = m_current_offset;
|
|
return true;
|
|
}
|
|
|
|
// if m_current_offset <= 0, we've got nothing else to try
|
|
if (m_current_offset <= 0)
|
|
return false;
|
|
|
|
// check pc - 1 to see if it's valid
|
|
Address pc_minus_one (m_current_pc);
|
|
pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1);
|
|
if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one))
|
|
{
|
|
// *valid_pc_offset = m_current_offset - 1;
|
|
valid_pc_offset = m_current_pc.GetOffset() - 1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
|
|
// executing frame.
|
|
|
|
void
|
|
RegisterContextLLDB::InitializeZerothFrame()
|
|
{
|
|
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();
|
|
|
|
if (reg_ctx_sp.get() == NULL)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("frame does not have a register context");
|
|
return;
|
|
}
|
|
|
|
addr_t current_pc = reg_ctx_sp->GetPC();
|
|
|
|
if (current_pc == LLDB_INVALID_ADDRESS)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("frame does not have a pc");
|
|
return;
|
|
}
|
|
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
|
|
// 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.
|
|
// (which would be a no-op in frame 0 where we get it from the register set,
|
|
// but still a good idea to make the call here for other ABIs that may exist.)
|
|
ABI *abi = process->GetABI().get();
|
|
if (abi)
|
|
current_pc = abi->FixCodeAddress(current_pc);
|
|
|
|
// Initialize m_current_pc, an Address object, based on current_pc, an addr_t.
|
|
m_current_pc.SetLoadAddress (current_pc, &process->GetTarget());
|
|
|
|
// 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.
|
|
ModuleSP pc_module_sp (m_current_pc.GetModule());
|
|
if (!m_current_pc.IsValid() || !pc_module_sp)
|
|
{
|
|
UnwindLogMsg ("using architectural default unwind method");
|
|
}
|
|
|
|
// We require either a symbol or function in the symbols context to be successfully
|
|
// filled in or this context is of no use to us.
|
|
const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
|
|
if (pc_module_sp.get()
|
|
&& (pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, resolve_scope, m_sym_ctx) & resolve_scope))
|
|
{
|
|
m_sym_ctx_valid = true;
|
|
}
|
|
|
|
if (m_sym_ctx.symbol)
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
|
|
current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
}
|
|
else if (m_sym_ctx.function)
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'",
|
|
current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", current_pc);
|
|
}
|
|
|
|
AddressRange addr_range;
|
|
m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range);
|
|
|
|
if (IsTrapHandlerSymbol (process, m_sym_ctx))
|
|
{
|
|
m_frame_type = eTrapHandlerFrame;
|
|
}
|
|
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 (m_current_pc.GetSection() == m_start_pc.GetSection())
|
|
{
|
|
m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset();
|
|
}
|
|
else if (m_current_pc.GetModule() == m_start_pc.GetModule())
|
|
{
|
|
// This means that whatever symbol we kicked up isn't really correct
|
|
// --- we should not 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 = m_current_pc.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 ();
|
|
|
|
UnwindPlan::RowSP active_row;
|
|
lldb::RegisterKind row_register_kind = eRegisterKindGeneric;
|
|
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.get() && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg ("%s", active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
|
|
if (!active_row.get())
|
|
{
|
|
UnwindLogMsg ("could not find an unwindplan row for this frame's pc");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
|
|
if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa))
|
|
{
|
|
// Try the fall back unwind plan since the
|
|
// full unwind plan failed.
|
|
FuncUnwindersSP func_unwinders_sp;
|
|
UnwindPlanSP call_site_unwind_plan;
|
|
bool cfa_status = false;
|
|
|
|
if (m_sym_ctx_valid)
|
|
{
|
|
func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
|
}
|
|
|
|
if(func_unwinders_sp.get() != nullptr)
|
|
call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
|
|
|
|
if (call_site_unwind_plan.get() != nullptr)
|
|
{
|
|
m_fallback_unwind_plan_sp = call_site_unwind_plan;
|
|
if(TryFallbackUnwindPlan())
|
|
cfa_status = true;
|
|
}
|
|
if (!cfa_status)
|
|
{
|
|
UnwindLogMsg ("could not read CFA value for first frame.");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
|
|
UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan",
|
|
(uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()),
|
|
(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()
|
|
{
|
|
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (IsFrameZero ())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("non-zeroth frame tests positive for IsFrameZero -- that shouldn't happen.");
|
|
return;
|
|
}
|
|
|
|
if (!GetNextFrame().get() || !GetNextFrame()->IsValid())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("Could not get next frame, marking this frame as invalid.");
|
|
return;
|
|
}
|
|
if (!m_thread.GetRegisterContext())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("Could not get register context for this thread, marking this frame as invalid.");
|
|
return;
|
|
}
|
|
|
|
addr_t pc;
|
|
if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
|
|
{
|
|
UnwindLogMsg ("could not get pc value");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (log)
|
|
{
|
|
UnwindLogMsg ("pc = 0x%" PRIx64, pc);
|
|
addr_t reg_val;
|
|
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
|
|
UnwindLogMsg ("fp = 0x%" PRIx64, reg_val);
|
|
if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
|
|
UnwindLogMsg ("sp = 0x%" PRIx64, reg_val);
|
|
}
|
|
|
|
// A pc of 0x0 means it's the end of the stack crawl unless we're above a trap handler function
|
|
bool above_trap_handler = false;
|
|
if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame())
|
|
above_trap_handler = true;
|
|
|
|
if (pc == 0 || pc == 0x1)
|
|
{
|
|
if (above_trap_handler == false)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("this frame has a pc of 0x0");
|
|
return;
|
|
}
|
|
}
|
|
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
// 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 = process->GetABI().get();
|
|
if (abi)
|
|
pc = abi->FixCodeAddress(pc);
|
|
|
|
m_current_pc.SetLoadAddress (pc, &process->GetTarget());
|
|
|
|
// 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.
|
|
ModuleSP pc_module_sp (m_current_pc.GetModule());
|
|
if (!m_current_pc.IsValid() || !pc_module_sp)
|
|
{
|
|
UnwindLogMsg ("using architectural default unwind method");
|
|
|
|
// Test the pc value to see if we know it's in an unmapped/non-executable region of memory.
|
|
uint32_t permissions;
|
|
if (process->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())
|
|
{
|
|
UnwindLogMsg ("had a pc of 0x%" PRIx64 " which is not in executable memory but on frame 1 -- allowing it once.",
|
|
(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;
|
|
UnwindLogMsg ("pc is in a non-executable section of memory and this isn't the 2nd frame in the stack walk.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0);
|
|
if (row.get())
|
|
{
|
|
if (!ReadCFAValueForRow (row_register_kind, row, m_cfa))
|
|
{
|
|
UnwindLogMsg ("failed to get cfa value");
|
|
if (m_frame_type != eSkipFrame) // don't override eSkipFrame
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// A couple of sanity checks..
|
|
if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1)
|
|
{
|
|
UnwindLogMsg ("could not find a valid cfa address");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
// m_cfa should point into the stack memory; if we can query memory region permissions,
|
|
// see if the memory is allocated & readable.
|
|
if (process->GetLoadAddressPermissions(m_cfa, permissions)
|
|
&& (permissions & ePermissionsReadable) == 0)
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("the CFA points to a region of memory that is not readable");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("could not find a row for function offset zero");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (CheckIfLoopingStack ())
|
|
{
|
|
TryFallbackUnwindPlan();
|
|
if (CheckIfLoopingStack ())
|
|
{
|
|
UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
|
|
UnwindLogMsg ("initialized frame cfa is 0x%" PRIx64, (uint64_t) m_cfa);
|
|
return;
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("could not find any symbol for this pc, or a default unwind plan, to continue unwind.");
|
|
return;
|
|
}
|
|
|
|
bool resolve_tail_call_address = true; // m_current_pc can be one past the address range of the function...
|
|
// This will handle the case where the saved pc does not point to
|
|
// a function/symbol because it is beyond the bounds of the correct
|
|
// function and there's no symbol there. ResolveSymbolContextForAddress
|
|
// will fail to find a symbol, back up the pc by 1 and re-search.
|
|
const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
|
|
uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress (m_current_pc,
|
|
resolve_scope,
|
|
m_sym_ctx, resolve_tail_call_address);
|
|
|
|
// We require either a symbol or function in the symbols context to be successfully
|
|
// filled in or this context is of no use to us.
|
|
if (resolve_scope & resolved_scope)
|
|
{
|
|
m_sym_ctx_valid = true;
|
|
}
|
|
|
|
if (m_sym_ctx.symbol)
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
|
|
pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
}
|
|
else if (m_sym_ctx.function)
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'",
|
|
pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", pc);
|
|
}
|
|
|
|
AddressRange addr_range;
|
|
if (!m_sym_ctx.GetAddressRange (resolve_scope, 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 != eTrapHandlerFrame
|
|
&& 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)
|
|
{
|
|
UnwindLogMsg ("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s",
|
|
pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
Address temporary_pc;
|
|
temporary_pc.SetLoadAddress (pc - 1, &process->GetTarget());
|
|
m_sym_ctx.Clear (false);
|
|
m_sym_ctx_valid = false;
|
|
uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
|
|
|
|
ModuleSP temporary_module_sp = temporary_pc.GetModule();
|
|
if (temporary_module_sp &&
|
|
temporary_module_sp->ResolveSymbolContextForAddress (temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope)
|
|
{
|
|
if (m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range))
|
|
m_sym_ctx_valid = true;
|
|
}
|
|
UnwindLogMsg ("Symbol is now %s", GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
}
|
|
|
|
// 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 = pc - m_start_pc.GetLoadAddress (&process->GetTarget());
|
|
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--;
|
|
if (m_sym_ctx_valid)
|
|
{
|
|
m_current_pc.SetLoadAddress (pc - 1, &process->GetTarget());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_start_pc = m_current_pc;
|
|
m_current_offset = -1;
|
|
m_current_offset_backed_up_one = -1;
|
|
}
|
|
|
|
if (IsTrapHandlerSymbol (process, m_sym_ctx))
|
|
{
|
|
m_frame_type = eTrapHandlerFrame;
|
|
}
|
|
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 ();
|
|
|
|
UnwindPlan::RowSP active_row;
|
|
RegisterKind row_register_kind = eRegisterKindGeneric;
|
|
|
|
// 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.get() && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame ();
|
|
int valid_offset = -1;
|
|
if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp, valid_offset))
|
|
{
|
|
active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (valid_offset);
|
|
row_register_kind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
if (active_row.get() && log)
|
|
{
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!active_row.get())
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg ("could not find unwind row for this pc");
|
|
return;
|
|
}
|
|
|
|
if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa))
|
|
{
|
|
UnwindLogMsg ("failed to get cfa");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
UnwindLogMsg ("m_cfa = 0x%" PRIx64, m_cfa);
|
|
|
|
if (CheckIfLoopingStack ())
|
|
{
|
|
TryFallbackUnwindPlan();
|
|
if (CheckIfLoopingStack ())
|
|
{
|
|
UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
}
|
|
|
|
UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64,
|
|
(uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa);
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::CheckIfLoopingStack ()
|
|
{
|
|
// 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.
|
|
// To detect when we have the same CFA value multiple times, we compare the CFA of the current
|
|
// frame with the 2nd next frame because in some specail case (e.g. signal hanlders, hand
|
|
// written assembly without ABI compiance) we can have 2 frames with the same CFA (in theory we
|
|
// can have arbitrary number of frames with the same CFA, but more then 2 is very very unlikely)
|
|
|
|
RegisterContextLLDB::SharedPtr next_frame = GetNextFrame();
|
|
if (next_frame)
|
|
{
|
|
RegisterContextLLDB::SharedPtr next_next_frame = next_frame->GetNextFrame();
|
|
addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
|
|
if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa))
|
|
{
|
|
if (next_next_frame_cfa == m_cfa)
|
|
{
|
|
// We have a loop in the stack unwind
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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 eTrapHandlerFrame/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;
|
|
ModuleSP pc_module_sp (m_current_pc.GetModule());
|
|
|
|
if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL)
|
|
return unwind_plan_sp;
|
|
|
|
if (IsFrameZero ())
|
|
return unwind_plan_sp;
|
|
|
|
FuncUnwindersSP func_unwinders_sp (pc_module_sp->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 == eTrapHandlerFrame || m_frame_type == eDebuggerFrame)
|
|
return unwind_plan_sp;
|
|
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (*m_thread.CalculateTarget(), m_thread);
|
|
if (unwind_plan_sp)
|
|
{
|
|
if (unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (log && log->GetVerbose())
|
|
{
|
|
if (m_fast_unwind_plan_sp)
|
|
UnwindLogMsgVerbose ("frame, and has a fast UnwindPlan");
|
|
else
|
|
UnwindLogMsgVerbose ("frame");
|
|
}
|
|
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 eTrapHandlerFrame/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;
|
|
UnwindPlanSP arch_default_unwind_plan_sp;
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
ABI *abi = process ? process->GetABI().get() : NULL;
|
|
if (abi)
|
|
{
|
|
arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("unable to get architectural default UnwindPlan from ABI plugin");
|
|
}
|
|
|
|
bool behaves_like_zeroth_frame = false;
|
|
if (IsFrameZero ()
|
|
|| GetNextFrame()->m_frame_type == eTrapHandlerFrame
|
|
|| 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.
|
|
// Note, if we have a symbol context & a symbol, we don't want to follow this code path. This is
|
|
// for jumping to memory regions without any information available.
|
|
|
|
if ((!m_sym_ctx_valid || (m_sym_ctx.function == NULL && m_sym_ctx.symbol == NULL)) && behaves_like_zeroth_frame && m_current_pc.IsValid())
|
|
{
|
|
uint32_t permissions;
|
|
addr_t current_pc_addr = m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr());
|
|
if (current_pc_addr == 0
|
|
|| (process &&
|
|
process->GetLoadAddressPermissions (current_pc_addr, permissions)
|
|
&& (permissions & ePermissionsExecutable) == 0))
|
|
{
|
|
if (abi)
|
|
{
|
|
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.
|
|
ModuleSP pc_module_sp (m_current_pc.GetModule());
|
|
if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL)
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
return arch_default_unwind_plan_sp;
|
|
}
|
|
|
|
FuncUnwindersSP func_unwinders_sp;
|
|
if (m_sym_ctx_valid)
|
|
{
|
|
func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
|
}
|
|
|
|
// No FuncUnwinders available for this pc (stripped function symbols, lldb could not augment its
|
|
// function table with another source, like LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO).
|
|
// See if eh_frame or the .ARM.exidx tables have unwind information for this address, else fall
|
|
// back to the architectural default unwind.
|
|
if (!func_unwinders_sp)
|
|
{
|
|
m_frame_type = eNormalFrame;
|
|
|
|
if (!pc_module_sp || !pc_module_sp->GetObjectFile() || !m_current_pc.IsValid())
|
|
return arch_default_unwind_plan_sp;
|
|
|
|
// Even with -fomit-frame-pointer, we can try eh_frame to get back on track.
|
|
DWARFCallFrameInfo *eh_frame = pc_module_sp->GetObjectFile()->GetUnwindTable().GetEHFrameInfo();
|
|
if (eh_frame)
|
|
{
|
|
unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (eh_frame->GetUnwindPlan (m_current_pc, *unwind_plan_sp))
|
|
return unwind_plan_sp;
|
|
else
|
|
unwind_plan_sp.reset();
|
|
}
|
|
|
|
ArmUnwindInfo *arm_exidx = pc_module_sp->GetObjectFile()->GetUnwindTable().GetArmUnwindInfo();
|
|
if (arm_exidx)
|
|
{
|
|
unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (arm_exidx->GetUnwindPlan (exe_ctx.GetTargetRef(), m_current_pc, *unwind_plan_sp))
|
|
return unwind_plan_sp;
|
|
else
|
|
unwind_plan_sp.reset();
|
|
}
|
|
|
|
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 == eTrapHandlerFrame && process)
|
|
{
|
|
m_fast_unwind_plan_sp.reset();
|
|
unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc) && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
|
|
{
|
|
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 (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo (m_sym_ctx))
|
|
{
|
|
// We must specifically call the GetEHFrameUnwindPlan() method here -- normally we would
|
|
// call GetUnwindPlanAtCallSite() -- because CallSite may return an unwind plan sourced from
|
|
// either eh_frame (that's what we intend) or compact unwind (this won't work)
|
|
unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it",
|
|
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 && process)
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (process->GetTarget(), m_thread, m_current_offset_backed_up_one);
|
|
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
|
{
|
|
if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo)
|
|
{
|
|
// We probably have an UnwindPlan created by inspecting assembly instructions. The
|
|
// assembly profilers work really well with compiler-generated functions but hand-
|
|
// written assembly can be problematic. We set the eh_frame based unwind plan as our
|
|
// fallback unwind plan if instruction emulation doesn't work out even for non call
|
|
// sites if it is available and use the architecture default unwind plan if it is
|
|
// not available. The eh_frame unwind plan is more reliable even on non call sites
|
|
// then the architecture default plan and for hand written assembly code it is often
|
|
// written in a way that it valid at all location what helps in the most common
|
|
// cases when the instruction emulation fails.
|
|
UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
|
|
if (call_site_unwind_plan &&
|
|
call_site_unwind_plan.get() != unwind_plan_sp.get() &&
|
|
call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
|
|
{
|
|
m_fallback_unwind_plan_sp = call_site_unwind_plan;
|
|
}
|
|
else
|
|
{
|
|
m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
|
|
}
|
|
}
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", 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
|
|
if (process)
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (process->GetTarget(), m_current_offset_backed_up_one);
|
|
}
|
|
int valid_offset = -1;
|
|
if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset))
|
|
{
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", 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.
|
|
if (process)
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (process->GetTarget(), m_thread, m_current_offset_backed_up_one);
|
|
}
|
|
if (unwind_plan_sp && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo)
|
|
{
|
|
// We probably have an UnwindPlan created by inspecting assembly instructions. The assembly
|
|
// profilers work really well with compiler-generated functions but hand- written assembly
|
|
// can be problematic. We set the eh_frame based unwind plan as our fallback unwind plan if
|
|
// instruction emulation doesn't work out even for non call sites if it is available and use
|
|
// the architecture default unwind plan if it is not available. The eh_frame unwind plan is
|
|
// more reliable even on non call sites then the architecture default plan and for hand
|
|
// written assembly code it is often written in a way that it valid at all location what
|
|
// helps in the most common cases when the instruction emulation fails.
|
|
UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
|
|
if (call_site_unwind_plan &&
|
|
call_site_unwind_plan.get() != unwind_plan_sp.get() &&
|
|
call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
|
|
{
|
|
m_fallback_unwind_plan_sp = call_site_unwind_plan;
|
|
}
|
|
else
|
|
{
|
|
m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
|
|
}
|
|
}
|
|
|
|
if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset))
|
|
{
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
// If we're on the first instruction of a function, and we have an architectural default UnwindPlan
|
|
// for the initial instruction of a function, use that.
|
|
if (m_current_offset_backed_up_one == 0)
|
|
{
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry (m_thread);
|
|
if (unwind_plan_sp)
|
|
{
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
|
|
return unwind_plan_sp;
|
|
}
|
|
}
|
|
|
|
// If nothing else, use the architectural default UnwindPlan and hope that does the job.
|
|
if (arch_default_unwind_plan_sp)
|
|
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", arch_default_unwind_plan_sp->GetSourceName().GetCString());
|
|
else
|
|
UnwindLogMsg ("Unable to find any UnwindPlan for full unwind of this frame.");
|
|
|
|
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 (size_t reg)
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg);
|
|
}
|
|
|
|
size_t
|
|
RegisterContextLLDB::GetRegisterSetCount ()
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterSetCount ();
|
|
}
|
|
|
|
const RegisterSet *
|
|
RegisterContextLLDB::GetRegisterSet (size_t reg_set)
|
|
{
|
|
return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
|
|
}
|
|
|
|
uint32_t
|
|
RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (lldb::RegisterKind 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::eRegisterInLiveRegisterContext:
|
|
{
|
|
const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
|
|
|
|
if (!other_reg_info)
|
|
return false;
|
|
|
|
success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value);
|
|
}
|
|
break;
|
|
case UnwindLLDB::RegisterLocation::eRegisterInRegister:
|
|
{
|
|
const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
|
|
|
|
if (!other_reg_info)
|
|
return false;
|
|
|
|
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::eRegisterInLiveRegisterContext:
|
|
{
|
|
const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number);
|
|
success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value);
|
|
}
|
|
break;
|
|
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;
|
|
}
|
|
|
|
// After the final stack frame in a stack walk we'll get one invalid (eNotAValidFrame) stack frame --
|
|
// one past the end of the stack walk. But higher-level code will need to tell the differnece between
|
|
// "the unwind plan below this frame failed" versus "we successfully completed the stack walk" so
|
|
// this method helps to disambiguate that.
|
|
|
|
bool
|
|
RegisterContextLLDB::IsTrapHandlerFrame () const
|
|
{
|
|
return m_frame_type == eTrapHandlerFrame;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::IsTrapHandlerSymbol (lldb_private::Process *process, const lldb_private::SymbolContext &m_sym_ctx) const
|
|
{
|
|
PlatformSP platform_sp (process->GetTarget().GetPlatform());
|
|
if (platform_sp)
|
|
{
|
|
const std::vector<ConstString> trap_handler_names (platform_sp->GetTrapHandlerSymbolNames());
|
|
for (ConstString name : trap_handler_names)
|
|
{
|
|
if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
|
|
(m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
const std::vector<ConstString> user_specified_trap_handler_names (m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames());
|
|
for (ConstString name : user_specified_trap_handler_names)
|
|
{
|
|
if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
|
|
(m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?
|
|
|
|
enum UnwindLLDB::RegisterSearchResult
|
|
RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc)
|
|
{
|
|
RegisterNumber regnum (m_thread, eRegisterKindLLDB, lldb_regnum);
|
|
|
|
// 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 (regnum.GetAsKind (eRegisterKindLLDB));
|
|
if (iterator != m_registers.end())
|
|
{
|
|
regloc = iterator->second;
|
|
UnwindLogMsg ("supplying caller's saved %s (%d)'s location, cached",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
}
|
|
|
|
// Look through the available UnwindPlans for the register location.
|
|
|
|
UnwindPlan::Row::RegisterLocation unwindplan_regloc;
|
|
bool have_unwindplan_regloc = false;
|
|
RegisterKind unwindplan_registerkind = kNumRegisterKinds;
|
|
|
|
if (m_fast_unwind_plan_sp)
|
|
{
|
|
UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind ();
|
|
if (regnum.GetAsKind (unwindplan_registerkind) == LLDB_INVALID_REGNUM)
|
|
{
|
|
UnwindLogMsg ("could not convert lldb regnum %s (%d) into %d RegisterKind reg numbering scheme",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), (int) unwindplan_registerkind);
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
if (active_row->GetRegisterInfo (regnum.GetAsKind (unwindplan_registerkind), unwindplan_regloc))
|
|
{
|
|
UnwindLogMsg ("supplying caller's saved %s (%d)'s location using FastUnwindPlan",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
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)
|
|
{
|
|
RegisterNumber pc_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
|
|
UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
|
|
RegisterNumber return_address_reg;
|
|
|
|
// If we're fetching the saved pc and this UnwindPlan defines a ReturnAddress register (e.g. lr on arm),
|
|
// look for the return address register number in the UnwindPlan's row.
|
|
if (pc_regnum.IsValid()
|
|
&& pc_regnum == regnum
|
|
&& m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM)
|
|
{
|
|
|
|
return_address_reg.init (m_thread, m_full_unwind_plan_sp->GetRegisterKind(), m_full_unwind_plan_sp->GetReturnAddressRegister());
|
|
regnum = return_address_reg;
|
|
UnwindLogMsg ("requested caller's saved PC but this UnwindPlan uses a RA reg; getting %s (%d) instead",
|
|
return_address_reg.GetName(), return_address_reg.GetAsKind (eRegisterKindLLDB));
|
|
}
|
|
else
|
|
{
|
|
if (regnum.GetAsKind (unwindplan_registerkind) == LLDB_INVALID_REGNUM)
|
|
{
|
|
if (unwindplan_registerkind == eRegisterKindGeneric)
|
|
{
|
|
UnwindLogMsg ("could not convert lldb regnum %s (%d) into eRegisterKindGeneric reg numbering scheme",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("could not convert lldb regnum %s (%d) into %d RegisterKind reg numbering scheme",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), (int) unwindplan_registerkind);
|
|
}
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
}
|
|
|
|
if (regnum.IsValid()
|
|
&& active_row->GetRegisterInfo (regnum.GetAsKind (unwindplan_registerkind), unwindplan_regloc))
|
|
{
|
|
have_unwindplan_regloc = true;
|
|
UnwindLogMsg ("supplying caller's saved %s (%d)'s location using %s UnwindPlan",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
|
|
// This is frame 0 and we're retrieving the PC and it's saved in a Return Address register and
|
|
// it hasn't been saved anywhere yet -- that is, it's still live in the actual register.
|
|
// Handle this specially.
|
|
|
|
if (have_unwindplan_regloc == false
|
|
&& return_address_reg.IsValid()
|
|
&& IsFrameZero())
|
|
{
|
|
if (return_address_reg.GetAsKind (eRegisterKindLLDB) != LLDB_INVALID_REGNUM)
|
|
{
|
|
lldb_private::UnwindLLDB::RegisterLocation new_regloc;
|
|
new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext;
|
|
new_regloc.location.register_number = return_address_reg.GetAsKind (eRegisterKindLLDB);
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc;
|
|
regloc = new_regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d) from the live RegisterContext at frame 0, saved in %d",
|
|
return_address_reg.GetName(), return_address_reg.GetAsKind (eRegisterKindLLDB),
|
|
return_address_reg.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
}
|
|
|
|
// If this architecture stores the return address in a register (it defines a Return Address register)
|
|
// and we're on a non-zero stack frame and the Full UnwindPlan says that the pc is stored in the
|
|
// RA registers (e.g. lr on arm), then we know that the full unwindplan is not trustworthy -- this
|
|
// is an impossible situation and the instruction emulation code has likely been misled.
|
|
// If this stack frame meets those criteria, we need to throw away the Full UnwindPlan that the
|
|
// instruction emulation came up with and fall back to the architecture's Default UnwindPlan so
|
|
// the stack walk can get past this point.
|
|
|
|
// Special note: If the Full UnwindPlan was generated from the compiler, don't second-guess it
|
|
// when we're at a call site location.
|
|
|
|
// arch_default_ra_regnum is the return address register # in the Full UnwindPlan register numbering
|
|
RegisterNumber arch_default_ra_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
|
|
|
|
if (arch_default_ra_regnum.GetAsKind (unwindplan_registerkind) != LLDB_INVALID_REGNUM
|
|
&& pc_regnum == regnum
|
|
&& unwindplan_regloc.IsInOtherRegister()
|
|
&& unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum.GetAsKind (unwindplan_registerkind)
|
|
&& m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes
|
|
&& !m_all_registers_available)
|
|
{
|
|
UnwindLogMsg ("%s UnwindPlan tried to restore the pc from the link register but this is a non-zero frame",
|
|
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
|
|
// Throw away the full unwindplan; install the arch default unwindplan
|
|
if (ForceSwitchToFallbackUnwindPlan())
|
|
{
|
|
// Update for the possibly new unwind plan
|
|
unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind ();
|
|
UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
|
|
// Sanity check: Verify that we can fetch a pc value and CFA value with this unwind plan
|
|
|
|
RegisterNumber arch_default_pc_reg (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
bool can_fetch_pc_value = false;
|
|
bool can_fetch_cfa = false;
|
|
addr_t cfa_value;
|
|
if (active_row)
|
|
{
|
|
if (arch_default_pc_reg.GetAsKind (unwindplan_registerkind) != LLDB_INVALID_REGNUM
|
|
&& active_row->GetRegisterInfo (arch_default_pc_reg.GetAsKind (unwindplan_registerkind), unwindplan_regloc))
|
|
{
|
|
can_fetch_pc_value = true;
|
|
}
|
|
if (ReadCFAValueForRow (unwindplan_registerkind, active_row, cfa_value))
|
|
{
|
|
can_fetch_cfa = true;
|
|
}
|
|
}
|
|
|
|
if (can_fetch_pc_value && can_fetch_cfa)
|
|
{
|
|
have_unwindplan_regloc = true;
|
|
}
|
|
else
|
|
{
|
|
have_unwindplan_regloc = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We were unable to fall back to another unwind plan
|
|
have_unwindplan_regloc = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (have_unwindplan_regloc == false)
|
|
{
|
|
// Did the UnwindPlan fail to give us the caller's stack pointer?
|
|
// The stack pointer is defined to be the same as THIS frame's CFA, so return the CFA value as
|
|
// the caller's stack pointer. This is true on x86-32/x86-64 at least.
|
|
|
|
RegisterNumber sp_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
|
|
if (sp_regnum.GetAsKind (eRegisterKindLLDB) != LLDB_INVALID_REGNUM
|
|
&& sp_regnum.GetAsKind (eRegisterKindLLDB) == regnum.GetAsKind (eRegisterKindLLDB))
|
|
{
|
|
// 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[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's stack pointer %s (%d) value, computed from CFA",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
}
|
|
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
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 = process ? process->GetABI().get() : NULL;
|
|
if (abi)
|
|
{
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(regnum.GetAsKind (eRegisterKindLLDB));
|
|
if (reg_info && abi->RegisterIsVolatile (reg_info))
|
|
{
|
|
UnwindLogMsg ("did not supply reg location for %s (%d) because it is volatile",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
|
|
}
|
|
}
|
|
|
|
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::eRegisterInLiveRegisterContext;
|
|
new_regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB);
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc;
|
|
regloc = new_regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d) from the live RegisterContext at frame 0",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
else
|
|
{
|
|
std::string unwindplan_name ("");
|
|
if (m_full_unwind_plan_sp)
|
|
{
|
|
unwindplan_name += "via '";
|
|
unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString();
|
|
unwindplan_name += "'";
|
|
}
|
|
UnwindLogMsg ("no save location for %s (%d) %s",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
unwindplan_name.c_str());
|
|
}
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
|
|
// 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[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc;
|
|
UnwindLogMsg ("save location for %s (%d) is unspecified, continue searching",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsSame())
|
|
{
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
|
|
regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB);
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsCFAPlusOffset())
|
|
{
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
|
|
regloc.location.inferred_value = m_cfa + offset;
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d), value is CFA plus offset %d [value is 0x%" PRIx64 "]",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
offset, regloc.location.inferred_value);
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsAtCFAPlusOffset())
|
|
{
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
|
|
regloc.location.target_memory_location = m_cfa + offset;
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d) from the stack, saved at CFA plus offset %d [saved at 0x%" PRIx64 "]",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
offset, regloc.location.target_memory_location);
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsInOtherRegister())
|
|
{
|
|
uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
|
|
RegisterNumber row_regnum (m_thread, unwindplan_registerkind, unwindplan_regnum);
|
|
if (row_regnum.GetAsKind (eRegisterKindLLDB) == LLDB_INVALID_REGNUM)
|
|
{
|
|
UnwindLogMsg ("could not supply caller's %s (%d) location - was saved in another reg but couldn't convert that regnum",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister;
|
|
regloc.location.register_number = row_regnum.GetAsKind (eRegisterKindLLDB);
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB),
|
|
row_regnum.GetName(), row_regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression())
|
|
{
|
|
DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(),
|
|
unwindplan_regloc.GetDWARFExpressionLength(),
|
|
process->GetByteOrder(), process->GetAddressByteSize());
|
|
ModuleSP opcode_ctx;
|
|
DWARFExpression dwarfexpr (opcode_ctx,
|
|
dwarfdata,
|
|
nullptr,
|
|
0,
|
|
unwindplan_regloc.GetDWARFExpressionLength());
|
|
dwarfexpr.SetRegisterKind (unwindplan_registerkind);
|
|
Value result;
|
|
Error error;
|
|
if (dwarfexpr.Evaluate (&exe_ctx, 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[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d) via DWARF expression (IsDWARFExpression)",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
else
|
|
{
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
|
|
regloc.location.target_memory_location = val;
|
|
m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg ("supplying caller's register %s (%d) via DWARF expression (IsAtDWARFExpression)",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
}
|
|
UnwindLogMsg ("tried to use IsDWARFExpression or IsAtDWARFExpression for %s (%d) but failed",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
|
|
UnwindLogMsg ("no save location for %s (%d) in this stack frame",
|
|
regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB));
|
|
|
|
// FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported.
|
|
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
}
|
|
|
|
// TryFallbackUnwindPlan() -- this method is a little tricky.
|
|
//
|
|
// When this is called, the frame above -- the caller frame, the "previous" frame --
|
|
// is invalid or bad.
|
|
//
|
|
// Instead of stopping the stack walk here, we'll try a different UnwindPlan and see
|
|
// if we can get a valid frame above us.
|
|
//
|
|
// This most often happens when an unwind plan based on assembly instruction inspection
|
|
// is not correct -- mostly with hand-written assembly functions or functions where the
|
|
// stack frame is set up "out of band", e.g. the kernel saved the register context and
|
|
// then called an asynchronous trap handler like _sigtramp.
|
|
//
|
|
// Often in these cases, if we just do a dumb stack walk we'll get past this tricky
|
|
// frame and our usual techniques can continue to be used.
|
|
|
|
bool
|
|
RegisterContextLLDB::TryFallbackUnwindPlan ()
|
|
{
|
|
if (m_fallback_unwind_plan_sp.get() == nullptr)
|
|
return false;
|
|
|
|
if (m_full_unwind_plan_sp.get() == nullptr)
|
|
return false;
|
|
|
|
if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get()
|
|
|| m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If a compiler generated unwind plan failed, trying the arch default unwindplan
|
|
// isn't going to do any better.
|
|
if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
|
|
return false;
|
|
|
|
|
|
// Get the caller's pc value and our own CFA value.
|
|
// Swap in the fallback unwind plan, re-fetch the caller's pc value and CFA value.
|
|
// If they're the same, then the fallback unwind plan provides no benefit.
|
|
|
|
RegisterNumber pc_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
|
|
addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS;
|
|
addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS;
|
|
addr_t old_this_frame_cfa_value = m_cfa;
|
|
UnwindLLDB::RegisterLocation regloc;
|
|
if (SavedLocationForRegister (pc_regnum.GetAsKind (eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound)
|
|
{
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind (eRegisterKindLLDB));
|
|
if (reg_info)
|
|
{
|
|
RegisterValue reg_value;
|
|
if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
|
|
{
|
|
old_caller_pc_value = reg_value.GetAsUInt64();
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a tricky wrinkle! If SavedLocationForRegister() detects a really impossible
|
|
// register location for the full unwind plan, it may call ForceSwitchToFallbackUnwindPlan()
|
|
// which in turn replaces the full unwindplan with the fallback... in short, we're done,
|
|
// we're using the fallback UnwindPlan.
|
|
// We checked if m_fallback_unwind_plan_sp was nullptr at the top -- the only way it
|
|
// became nullptr since then is via SavedLocationForRegister().
|
|
if (m_fallback_unwind_plan_sp.get() == nullptr)
|
|
return true;
|
|
|
|
|
|
// Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide this isn't
|
|
// working, we need to restore.
|
|
// We'll also need to save & restore the value of the m_cfa ivar. Save is down below a bit in 'old_cfa'.
|
|
UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
|
|
addr_t old_cfa = m_cfa;
|
|
|
|
m_registers.clear();
|
|
|
|
m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
|
|
|
|
UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
|
|
if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified)
|
|
{
|
|
addr_t new_cfa;
|
|
if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa)
|
|
|| new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS)
|
|
{
|
|
UnwindLogMsg ("failed to get cfa with fallback unwindplan");
|
|
m_fallback_unwind_plan_sp.reset();
|
|
m_full_unwind_plan_sp = original_full_unwind_plan_sp;
|
|
m_cfa = old_cfa;
|
|
return false;
|
|
}
|
|
m_cfa = new_cfa;
|
|
|
|
if (SavedLocationForRegister (pc_regnum.GetAsKind (eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound)
|
|
{
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind (eRegisterKindLLDB));
|
|
if (reg_info)
|
|
{
|
|
RegisterValue reg_value;
|
|
if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
|
|
{
|
|
new_caller_pc_value = reg_value.GetAsUInt64();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (new_caller_pc_value == LLDB_INVALID_ADDRESS)
|
|
{
|
|
UnwindLogMsg ("failed to get a pc value for the caller frame with the fallback unwind plan");
|
|
m_fallback_unwind_plan_sp.reset();
|
|
m_full_unwind_plan_sp = original_full_unwind_plan_sp;
|
|
m_cfa = old_cfa;
|
|
return false;
|
|
}
|
|
|
|
if (old_caller_pc_value != LLDB_INVALID_ADDRESS)
|
|
{
|
|
if (old_caller_pc_value == new_caller_pc_value && new_cfa == old_this_frame_cfa_value)
|
|
{
|
|
UnwindLogMsg ("fallback unwind plan got the same values for this frame CFA and caller frame pc, not using");
|
|
m_fallback_unwind_plan_sp.reset();
|
|
m_full_unwind_plan_sp = original_full_unwind_plan_sp;
|
|
m_cfa = old_cfa;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UnwindLogMsg ("trying to unwind from this function with the UnwindPlan '%s' because UnwindPlan '%s' failed.",
|
|
m_fallback_unwind_plan_sp->GetSourceName().GetCString(),
|
|
original_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
|
|
// We've copied the fallback unwind plan into the full - now clear the fallback.
|
|
m_fallback_unwind_plan_sp.reset();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan ()
|
|
{
|
|
if (m_fallback_unwind_plan_sp.get() == NULL)
|
|
return false;
|
|
|
|
if (m_full_unwind_plan_sp.get() == NULL)
|
|
return false;
|
|
|
|
if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get()
|
|
|| m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
|
|
|
if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified)
|
|
{
|
|
addr_t new_cfa;
|
|
if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa)
|
|
|| new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS)
|
|
{
|
|
UnwindLogMsg ("failed to get cfa with fallback unwindplan");
|
|
m_fallback_unwind_plan_sp.reset();
|
|
return false;
|
|
}
|
|
|
|
m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
|
|
m_fallback_unwind_plan_sp.reset();
|
|
|
|
m_registers.clear();
|
|
|
|
m_cfa = new_cfa;
|
|
|
|
UnwindLogMsg ("switched unconditionally to the fallback unwindplan %s", m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadCFAValueForRow (lldb::RegisterKind row_register_kind,
|
|
const UnwindPlan::RowSP &row,
|
|
addr_t &cfa_value)
|
|
{
|
|
RegisterValue reg_value;
|
|
|
|
cfa_value = LLDB_INVALID_ADDRESS;
|
|
addr_t cfa_reg_contents;
|
|
|
|
switch (row->GetCFAValue().GetValueType())
|
|
{
|
|
case UnwindPlan::Row::CFAValue::isRegisterDereferenced:
|
|
{
|
|
RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber());
|
|
if (ReadGPRValue (cfa_reg, cfa_reg_contents))
|
|
{
|
|
const RegisterInfo *reg_info = GetRegisterInfoAtIndex (cfa_reg.GetAsKind (eRegisterKindLLDB));
|
|
RegisterValue reg_value;
|
|
if (reg_info)
|
|
{
|
|
Error error = ReadRegisterValueFromMemory(reg_info,
|
|
cfa_reg_contents,
|
|
reg_info->byte_size,
|
|
reg_value);
|
|
if (error.Success ())
|
|
{
|
|
cfa_value = reg_value.GetAsUInt64();
|
|
UnwindLogMsg ("CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64,
|
|
cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB),
|
|
cfa_reg_contents, cfa_value);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
UnwindLogMsg ("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.",
|
|
cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB),
|
|
cfa_reg_contents);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case UnwindPlan::Row::CFAValue::isRegisterPlusOffset:
|
|
{
|
|
RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber());
|
|
if (ReadGPRValue (cfa_reg, cfa_reg_contents))
|
|
{
|
|
if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1)
|
|
{
|
|
UnwindLogMsg ("Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
|
|
cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB),
|
|
cfa_reg_contents);
|
|
cfa_reg_contents = LLDB_INVALID_ADDRESS;
|
|
return false;
|
|
}
|
|
cfa_value = cfa_reg_contents + row->GetCFAValue().GetOffset();
|
|
UnwindLogMsg ("CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d",
|
|
cfa_value,
|
|
cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB),
|
|
cfa_reg_contents, row->GetCFAValue().GetOffset());
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case UnwindPlan::Row::CFAValue::isDWARFExpression:
|
|
{
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
DataExtractor dwarfdata (row->GetCFAValue().GetDWARFExpressionBytes(),
|
|
row->GetCFAValue().GetDWARFExpressionLength(),
|
|
process->GetByteOrder(), process->GetAddressByteSize());
|
|
ModuleSP opcode_ctx;
|
|
DWARFExpression dwarfexpr (opcode_ctx,
|
|
dwarfdata,
|
|
nullptr,
|
|
0,
|
|
row->GetCFAValue().GetDWARFExpressionLength());
|
|
dwarfexpr.SetRegisterKind (row_register_kind);
|
|
Value result;
|
|
Error error;
|
|
if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error))
|
|
{
|
|
cfa_value = result.GetScalar().ULongLong();
|
|
|
|
UnwindLogMsg ("CFA value set by DWARF expression is 0x%" PRIx64, cfa_value);
|
|
return true;
|
|
}
|
|
UnwindLogMsg ("Failed to set CFA value via DWARF expression: %s", error.AsCString());
|
|
break;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Retrieve a general purpose register value for THIS frame, 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 (lldb::RegisterKind 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;
|
|
}
|
|
|
|
bool pc_register = false;
|
|
uint32_t generic_regnum;
|
|
if (register_kind == eRegisterKindGeneric && regnum == LLDB_REGNUM_GENERIC_PC)
|
|
{
|
|
pc_register = true;
|
|
}
|
|
else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindGeneric, generic_regnum)
|
|
&& generic_regnum == LLDB_REGNUM_GENERIC_PC)
|
|
{
|
|
pc_register = true;
|
|
}
|
|
|
|
lldb_private::UnwindLLDB::RegisterLocation regloc;
|
|
if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, pc_register))
|
|
{
|
|
return false;
|
|
}
|
|
if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value))
|
|
{
|
|
value = reg_value.GetAsUInt64();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadGPRValue (const RegisterNumber ®num, addr_t &value)
|
|
{
|
|
return ReadGPRValue (regnum.GetRegisterKind(), regnum.GetRegisterNumber(), value);
|
|
}
|
|
|
|
// Find the value of a register in THIS frame
|
|
|
|
bool
|
|
RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
|
|
UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum);
|
|
|
|
// If this is the 0th frame, hand this over to the live register context
|
|
if (IsFrameZero ())
|
|
{
|
|
UnwindLogMsgVerbose ("passing along to the live register context for reg %d", 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, false))
|
|
return false;
|
|
|
|
return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value);
|
|
}
|
|
|
|
bool
|
|
RegisterContextLLDB::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value)
|
|
{
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
|
|
UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum);
|
|
|
|
// If this is the 0th frame, hand this over to the live register context
|
|
if (IsFrameZero ())
|
|
{
|
|
UnwindLogMsgVerbose ("passing along to the live register context for reg %d", 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, false))
|
|
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 (CalculateTarget().get());
|
|
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;
|
|
|
|
bool above_trap_handler = false;
|
|
if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame())
|
|
above_trap_handler = true;
|
|
|
|
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
|
|
&& above_trap_handler == false
|
|
&& (pc == 0 || pc == 1))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
RegisterContextLLDB::UnwindLogMsg (const char *fmt, ...)
|
|
{
|
|
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (log)
|
|
{
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
|
|
char *logmsg;
|
|
if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL)
|
|
{
|
|
if (logmsg)
|
|
free (logmsg);
|
|
va_end (args);
|
|
return;
|
|
}
|
|
va_end (args);
|
|
|
|
log->Printf ("%*sth%d/fr%u %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number,
|
|
logmsg);
|
|
free (logmsg);
|
|
}
|
|
}
|
|
|
|
void
|
|
RegisterContextLLDB::UnwindLogMsgVerbose (const char *fmt, ...)
|
|
{
|
|
Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
|
|
if (log && log->GetVerbose())
|
|
{
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
|
|
char *logmsg;
|
|
if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL)
|
|
{
|
|
if (logmsg)
|
|
free (logmsg);
|
|
va_end (args);
|
|
return;
|
|
}
|
|
va_end (args);
|
|
|
|
log->Printf ("%*sth%d/fr%u %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number,
|
|
logmsg);
|
|
free (logmsg);
|
|
}
|
|
}
|
|
|
|
|