2364 lines
93 KiB
C++
2364 lines
93 KiB
C++
//===-- RegisterContextUnwind.cpp -----------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Target/RegisterContextUnwind.h"
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/AddressRange.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Expression/DWARFExpressionList.h"
|
|
#include "lldb/Symbol/ArmUnwindInfo.h"
|
|
#include "lldb/Symbol/CallFrameInfo.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/Symbol/SymbolFile.h"
|
|
#include "lldb/Target/ABI.h"
|
|
#include "lldb/Target/DynamicLoader.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/LanguageRuntime.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 "lldb/Utility/DataBufferHeap.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/RegisterValue.h"
|
|
#include "lldb/Utility/VASPrintf.h"
|
|
#include "lldb/lldb-private.h"
|
|
#include <memory>
|
|
|
|
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();
|
|
}
|
|
|
|
RegisterContextUnwind::RegisterContextUnwind(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_afa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(),
|
|
m_current_offset(0), m_current_offset_backed_up_one(0),
|
|
m_behaves_like_zeroth_frame(false), 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 RegisterContextUnwind::IsUnwindPlanValidForCurrentPC(
|
|
lldb::UnwindPlanSP unwind_plan_sp) {
|
|
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
|
|
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)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Initialize a RegisterContextUnwind which is the first frame of a stack -- the
|
|
// zeroth frame or currently executing frame.
|
|
|
|
void RegisterContextUnwind::InitializeZerothFrame() {
|
|
Log *log = GetLog(LLDBLog::Unwind);
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();
|
|
|
|
if (reg_ctx_sp.get() == nullptr) {
|
|
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);
|
|
|
|
UnwindPlanSP lang_runtime_plan_sp = LanguageRuntime::GetRuntimeUnwindPlan(
|
|
m_thread, this, m_behaves_like_zeroth_frame);
|
|
if (lang_runtime_plan_sp.get()) {
|
|
UnwindLogMsg("This is an async frame");
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
|
|
AddressRange addr_range;
|
|
m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx, &addr_range);
|
|
|
|
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);
|
|
}
|
|
|
|
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 we have LanguageRuntime UnwindPlan for this unwind, use those
|
|
// rules to find the caller frame instead of the function's normal
|
|
// UnwindPlans. The full unwind plan for this frame will be
|
|
// the LanguageRuntime-provided unwind plan, and there will not be a
|
|
// fast unwind plan.
|
|
if (lang_runtime_plan_sp.get()) {
|
|
active_row =
|
|
lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset);
|
|
row_register_kind = lang_runtime_plan_sp->GetRegisterKind();
|
|
if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
|
|
m_cfa)) {
|
|
UnwindLogMsg("Cannot set cfa");
|
|
} else {
|
|
m_full_unwind_plan_sp = lang_runtime_plan_sp;
|
|
if (log) {
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, lang_runtime_plan_sp.get(), &m_thread,
|
|
m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg("async active row: %s", active_row_strm.GetData());
|
|
}
|
|
UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
|
|
UnwindLogMsg(
|
|
"initialized async frame current pc is 0x%" PRIx64
|
|
" cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
|
|
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
|
|
(uint64_t)m_cfa, (uint64_t)m_afa);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
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.GetData());
|
|
}
|
|
}
|
|
|
|
if (!active_row.get()) {
|
|
UnwindLogMsg("could not find an unwindplan row for this frame's pc");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), 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->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_thread);
|
|
|
|
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;
|
|
}
|
|
} else
|
|
ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
|
|
|
|
UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64
|
|
" afa is 0x%" PRIx64 " using %s UnwindPlan",
|
|
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
|
|
(uint64_t)m_cfa,
|
|
(uint64_t)m_afa,
|
|
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
}
|
|
|
|
// Initialize a RegisterContextUnwind for the non-zeroth frame -- rely on the
|
|
// RegisterContextUnwind "below" it to provide things like its current pc value.
|
|
|
|
void RegisterContextUnwind::InitializeNonZerothFrame() {
|
|
Log *log = GetLog(LLDBLog::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;
|
|
}
|
|
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
|
|
// Some languages may have a logical parent stack frame which is
|
|
// not a real stack frame, but the programmer would consider it to
|
|
// be the caller of the frame, e.g. Swift asynchronous frames.
|
|
//
|
|
// A LanguageRuntime may provide an UnwindPlan that is used in this
|
|
// stack trace base on the RegisterContext contents, intsead
|
|
// of the normal UnwindPlans we would use for the return-pc.
|
|
UnwindPlanSP lang_runtime_plan_sp = LanguageRuntime::GetRuntimeUnwindPlan(
|
|
m_thread, this, m_behaves_like_zeroth_frame);
|
|
if (lang_runtime_plan_sp.get()) {
|
|
UnwindLogMsg("This is an async frame");
|
|
}
|
|
|
|
addr_t pc;
|
|
if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
|
|
UnwindLogMsg("could not get pc value");
|
|
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 = process->GetABI().get();
|
|
if (abi)
|
|
pc = abi->FixCodeAddress(pc);
|
|
|
|
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) {
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg("this frame has a pc of 0x0");
|
|
return;
|
|
}
|
|
}
|
|
|
|
const bool allow_section_end = true;
|
|
m_current_pc.SetLoadAddress(pc, &process->GetTarget(), allow_section_end);
|
|
|
|
// 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 we're above a trap handler,
|
|
// we may be at a bogus address because we jumped through a bogus function
|
|
// pointer and trapped, so don't force the arch default unwind plan in that
|
|
// case.
|
|
ModuleSP pc_module_sp(m_current_pc.GetModule());
|
|
if ((!m_current_pc.IsValid() || !pc_module_sp) &&
|
|
above_trap_handler == false) {
|
|
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 =
|
|
std::make_shared<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 (!ReadFrameAddress(row_register_kind, row->GetCFAValue(), m_cfa)) {
|
|
UnwindLogMsg("failed to get cfa value");
|
|
if (m_frame_type != eSkipFrame) // don't override eSkipFrame
|
|
{
|
|
m_frame_type = eNotAValidFrame;
|
|
}
|
|
return;
|
|
}
|
|
|
|
ReadFrameAddress(row_register_kind, row->GetAFAValue(), m_afa);
|
|
|
|
// 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 " afa is 0x%" PRIx64,
|
|
(uint64_t)m_cfa, (uint64_t)m_afa);
|
|
return;
|
|
}
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg("could not find any symbol for this pc, or a default unwind "
|
|
"plan, to continue unwind.");
|
|
return;
|
|
}
|
|
|
|
AddressRange addr_range;
|
|
m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx, &addr_range);
|
|
|
|
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);
|
|
}
|
|
|
|
bool decr_pc_and_recompute_addr_range;
|
|
|
|
if (!m_sym_ctx_valid) {
|
|
// Always decrement and recompute if the symbol lookup failed
|
|
decr_pc_and_recompute_addr_range = true;
|
|
} else if (GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
|
|
GetNextFrame()->m_frame_type == eDebuggerFrame) {
|
|
// Don't decrement if we're "above" an asynchronous event like
|
|
// sigtramp.
|
|
decr_pc_and_recompute_addr_range = false;
|
|
} else if (!addr_range.GetBaseAddress().IsValid() ||
|
|
addr_range.GetBaseAddress().GetSection() != m_current_pc.GetSection() ||
|
|
addr_range.GetBaseAddress().GetOffset() != m_current_pc.GetOffset()) {
|
|
// If our "current" pc isn't the start of a function, decrement the pc
|
|
// if we're up the stack.
|
|
if (m_behaves_like_zeroth_frame)
|
|
decr_pc_and_recompute_addr_range = false;
|
|
else
|
|
decr_pc_and_recompute_addr_range = true;
|
|
} else if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
|
|
// Signal dispatch may set the return address of the handler it calls to
|
|
// point to the first byte of a return trampoline (like __kernel_rt_sigreturn),
|
|
// so do not decrement and recompute if the symbol we already found is a trap
|
|
// handler.
|
|
decr_pc_and_recompute_addr_range = false;
|
|
} else if (m_behaves_like_zeroth_frame) {
|
|
decr_pc_and_recompute_addr_range = false;
|
|
} else {
|
|
// Decrement to find the function containing the call.
|
|
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 = temporary_pc.ResolveFunctionScope(m_sym_ctx, &addr_range);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
UnwindPlan::RowSP active_row;
|
|
RegisterKind row_register_kind = eRegisterKindGeneric;
|
|
|
|
// If we have LanguageRuntime UnwindPlan for this unwind, use those
|
|
// rules to find the caller frame instead of the function's normal
|
|
// UnwindPlans. The full unwind plan for this frame will be
|
|
// the LanguageRuntime-provided unwind plan, and there will not be a
|
|
// fast unwind plan.
|
|
if (lang_runtime_plan_sp.get()) {
|
|
active_row =
|
|
lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset);
|
|
row_register_kind = lang_runtime_plan_sp->GetRegisterKind();
|
|
if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
|
|
m_cfa)) {
|
|
UnwindLogMsg("Cannot set cfa");
|
|
} else {
|
|
m_full_unwind_plan_sp = lang_runtime_plan_sp;
|
|
if (log) {
|
|
StreamString active_row_strm;
|
|
active_row->Dump(active_row_strm, lang_runtime_plan_sp.get(), &m_thread,
|
|
m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg("async active row: %s", active_row_strm.GetData());
|
|
}
|
|
UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
|
|
UnwindLogMsg(
|
|
"initialized async frame current pc is 0x%" PRIx64
|
|
" cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
|
|
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
|
|
(uint64_t)m_cfa, (uint64_t)m_afa);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We've set m_frame_type and m_sym_ctx before this call.
|
|
m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
|
|
|
|
// 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();
|
|
PropagateTrapHandlerFlagFromUnwindPlan(m_fast_unwind_plan_sp);
|
|
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("Using fast unwind plan '%s'",
|
|
m_fast_unwind_plan_sp->GetSourceName().AsCString());
|
|
UnwindLogMsg("active row: %s", active_row_strm.GetData());
|
|
}
|
|
} else {
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
|
|
if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp)) {
|
|
active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(
|
|
m_current_offset_backed_up_one);
|
|
row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
|
|
PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
|
|
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("Using full unwind plan '%s'",
|
|
m_full_unwind_plan_sp->GetSourceName().AsCString());
|
|
UnwindLogMsg("active row: %s", active_row_strm.GetData());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!active_row.get()) {
|
|
m_frame_type = eNotAValidFrame;
|
|
UnwindLogMsg("could not find unwind row for this pc");
|
|
return;
|
|
}
|
|
|
|
if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
|
|
UnwindLogMsg("failed to get cfa");
|
|
m_frame_type = eNotAValidFrame;
|
|
return;
|
|
}
|
|
|
|
ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);
|
|
|
|
UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
|
|
|
|
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 " afa is 0x%" PRIx64,
|
|
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
|
|
(uint64_t)m_cfa,
|
|
(uint64_t)m_afa);
|
|
}
|
|
|
|
bool RegisterContextUnwind::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 compliance) 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)
|
|
|
|
RegisterContextUnwind::SharedPtr next_frame = GetNextFrame();
|
|
if (next_frame) {
|
|
RegisterContextUnwind::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 RegisterContextUnwind::IsFrameZero() const { return m_frame_number == 0; }
|
|
|
|
bool RegisterContextUnwind::BehavesLikeZerothFrame() const {
|
|
if (m_frame_number == 0)
|
|
return true;
|
|
if (m_behaves_like_zeroth_frame)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// 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 RegisterContextUnwind::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() == nullptr)
|
|
return unwind_plan_sp;
|
|
|
|
if (IsFrameZero())
|
|
return unwind_plan_sp;
|
|
|
|
FuncUnwindersSP func_unwinders_sp(
|
|
pc_module_sp->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)) {
|
|
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 RegisterContextUnwind::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() : nullptr;
|
|
if (abi) {
|
|
arch_default_unwind_plan_sp =
|
|
std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
|
|
abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
|
|
} else {
|
|
UnwindLogMsg(
|
|
"unable to get architectural default UnwindPlan from ABI plugin");
|
|
}
|
|
|
|
if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
|
|
GetNextFrame()->m_frame_type == eDebuggerFrame) {
|
|
m_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 == nullptr && m_sym_ctx.symbol == nullptr)) &&
|
|
m_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 =
|
|
std::make_shared<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() == nullptr) {
|
|
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->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->GetUnwindTable().GetEHFrameInfo();
|
|
if (eh_frame) {
|
|
unwind_plan_sp = std::make_shared<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->GetUnwindTable().GetArmUnwindInfo();
|
|
if (arm_exidx) {
|
|
unwind_plan_sp = std::make_shared<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();
|
|
}
|
|
|
|
CallFrameInfo *object_file_unwind =
|
|
pc_module_sp->GetUnwindTable().GetObjectFileUnwindInfo();
|
|
if (object_file_unwind) {
|
|
unwind_plan_sp = std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
|
|
if (object_file_unwind->GetUnwindPlan(m_current_pc, *unwind_plan_sp))
|
|
return unwind_plan_sp;
|
|
else
|
|
unwind_plan_sp.reset();
|
|
}
|
|
|
|
return arch_default_unwind_plan_sp;
|
|
}
|
|
|
|
if (m_frame_type == eTrapHandlerFrame && process) {
|
|
m_fast_unwind_plan_sp.reset();
|
|
|
|
// On some platforms the unwind information for signal handlers is not
|
|
// present or correct. Give the platform plugins a chance to provide
|
|
// substitute plan. Otherwise, use eh_frame.
|
|
if (m_sym_ctx_valid) {
|
|
lldb::PlatformSP platform = process->GetTarget().GetPlatform();
|
|
unwind_plan_sp = platform->GetTrapHandlerUnwindPlan(
|
|
process->GetTarget().GetArchitecture().GetTriple(),
|
|
GetSymbolOrFunctionName(m_sym_ctx));
|
|
|
|
if (unwind_plan_sp)
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
unwind_plan_sp =
|
|
func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
|
|
if (!unwind_plan_sp)
|
|
unwind_plan_sp =
|
|
func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
|
|
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());
|
|
if (!unwind_plan_sp)
|
|
unwind_plan_sp =
|
|
func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
|
|
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 (m_behaves_like_zeroth_frame && process) {
|
|
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
|
|
process->GetTarget(), m_thread);
|
|
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_thread);
|
|
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 because this "
|
|
"is the non-call site unwind plan and this is a "
|
|
"zeroth frame",
|
|
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 == 0) {
|
|
unwind_plan_sp =
|
|
func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry(
|
|
m_thread);
|
|
if (unwind_plan_sp) {
|
|
UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we are at "
|
|
"the first instruction of a function",
|
|
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_thread);
|
|
}
|
|
if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp)) {
|
|
UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
|
|
"is the call-site unwind plan",
|
|
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);
|
|
}
|
|
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_thread);
|
|
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)) {
|
|
UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we "
|
|
"failed to find a call-site unwind plan that would work",
|
|
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 because we are falling back "
|
|
"to the arch default plan",
|
|
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 RegisterContextUnwind::InvalidateAllRegisters() {
|
|
m_frame_type = eNotAValidFrame;
|
|
}
|
|
|
|
size_t RegisterContextUnwind::GetRegisterCount() {
|
|
return m_thread.GetRegisterContext()->GetRegisterCount();
|
|
}
|
|
|
|
const RegisterInfo *RegisterContextUnwind::GetRegisterInfoAtIndex(size_t reg) {
|
|
return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
|
|
}
|
|
|
|
size_t RegisterContextUnwind::GetRegisterSetCount() {
|
|
return m_thread.GetRegisterContext()->GetRegisterSetCount();
|
|
}
|
|
|
|
const RegisterSet *RegisterContextUnwind::GetRegisterSet(size_t reg_set) {
|
|
return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
|
|
}
|
|
|
|
uint32_t RegisterContextUnwind::ConvertRegisterKindToRegisterNumber(
|
|
lldb::RegisterKind kind, uint32_t num) {
|
|
return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
|
|
kind, num);
|
|
}
|
|
|
|
bool RegisterContextUnwind::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:
|
|
llvm_unreachable("FIXME debugger inferior function call unwind");
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
|
|
Status error(ReadRegisterValueFromMemory(
|
|
reg_info, regloc.location.target_memory_location, reg_info->byte_size,
|
|
value));
|
|
success = error.Success();
|
|
} break;
|
|
default:
|
|
llvm_unreachable("Unknown RegisterLocation type.");
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool RegisterContextUnwind::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:
|
|
llvm_unreachable("FIXME debugger inferior function call unwind");
|
|
case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: {
|
|
Status error(WriteRegisterValueToMemory(
|
|
reg_info, regloc.location.target_memory_location, reg_info->byte_size,
|
|
value));
|
|
success = error.Success();
|
|
} break;
|
|
default:
|
|
llvm_unreachable("Unknown RegisterLocation type.");
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool RegisterContextUnwind::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 difference between "the unwind plan
|
|
// below this frame failed" versus "we successfully completed the stack walk"
|
|
// so this method helps to disambiguate that.
|
|
|
|
bool RegisterContextUnwind::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 RegisterContextUnwind::IsSkipFrame() const {
|
|
return m_frame_type == eSkipFrame;
|
|
}
|
|
|
|
bool RegisterContextUnwind::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
|
|
RegisterContextUnwind::SavedLocationForRegister(
|
|
uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) {
|
|
RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
|
|
Log *log = GetLog(LLDBLog::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(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;
|
|
}
|
|
// The architecture default unwind plan marks unknown registers as
|
|
// Undefined so that we don't forward them up the stack when a
|
|
// jitted stack frame may have overwritten them. But when the
|
|
// arch default unwind plan is used as the Fast Unwind Plan, we
|
|
// need to recognize this & switch over to the Full Unwind Plan
|
|
// to see what unwind rule that (more knoweldgeable, probably)
|
|
// UnwindPlan has. If the full UnwindPlan says the register
|
|
// location is Undefined, then it really is.
|
|
if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
|
|
unwindplan_regloc) &&
|
|
!unwindplan_regloc.IsUndefined()) {
|
|
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
|
|
bool got_new_full_unwindplan = false;
|
|
if (!m_full_unwind_plan_sp) {
|
|
m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
|
|
got_new_full_unwindplan = true;
|
|
}
|
|
|
|
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_backed_up_one);
|
|
unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
|
|
|
|
if (got_new_full_unwindplan && active_row.get() && log) {
|
|
StreamString active_row_strm;
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
|
|
&m_thread,
|
|
m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
|
|
UnwindLogMsg("Using full unwind plan '%s'",
|
|
m_full_unwind_plan_sp->GetSourceName().AsCString());
|
|
UnwindLogMsg("active row: %s", active_row_strm.GetData());
|
|
}
|
|
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) {
|
|
// If this is a trap handler frame, we should have access to
|
|
// the complete register context when the interrupt/async
|
|
// signal was received, we should fetch the actual saved $pc
|
|
// value instead of the Return Address register.
|
|
// If $pc is not available, fall back to the RA reg.
|
|
UnwindPlan::Row::RegisterLocation scratch;
|
|
if (m_frame_type == eTrapHandlerFrame &&
|
|
active_row->GetRegisterInfo
|
|
(pc_regnum.GetAsKind (unwindplan_registerkind), scratch)) {
|
|
UnwindLogMsg("Providing pc register instead of rewriting to "
|
|
"RA reg because this is a trap handler and there is "
|
|
"a location for the saved pc register value.");
|
|
} else {
|
|
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 && 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 (ReadFrameAddress(unwindplan_registerkind,
|
|
active_row->GetCFAValue(), cfa_value)) {
|
|
can_fetch_cfa = true;
|
|
}
|
|
}
|
|
|
|
have_unwindplan_regloc = can_fetch_pc_value && can_fetch_cfa;
|
|
} else {
|
|
// We were unable to fall back to another unwind plan
|
|
have_unwindplan_regloc = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
if (!have_unwindplan_regloc) {
|
|
// If the UnwindPlan failed to give us an unwind location for this
|
|
// register, we may be able to fall back to some ABI-defined default. For
|
|
// example, some ABIs allow to determine the caller's SP via the CFA. Also,
|
|
// the ABI may set volatile registers to the undefined state.
|
|
ABI *abi = process ? process->GetABI().get() : nullptr;
|
|
if (abi) {
|
|
const RegisterInfo *reg_info =
|
|
GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
|
|
if (reg_info &&
|
|
abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
|
|
UnwindLogMsg(
|
|
"supplying caller's saved %s (%d)'s location using ABI default",
|
|
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
|
|
have_unwindplan_regloc = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!have_unwindplan_regloc) {
|
|
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.IsUndefined()) {
|
|
UnwindLogMsg(
|
|
"did not supply reg location for %s (%d) because it is volatile",
|
|
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsSame()) {
|
|
if (!IsFrameZero() &&
|
|
(regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC ||
|
|
regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) {
|
|
UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or "
|
|
"return address reg on a non-zero frame -- treat as if we "
|
|
"have no information",
|
|
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
} else {
|
|
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.IsAFAPlusOffset()) {
|
|
if (m_afa == LLDB_INVALID_ADDRESS)
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred;
|
|
regloc.location.inferred_value = m_afa + offset;
|
|
m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg("supplying caller's register %s (%d), value is AFA plus "
|
|
"offset %d [value is 0x%" PRIx64 "]",
|
|
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
|
|
regloc.location.inferred_value);
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterFound;
|
|
}
|
|
|
|
if (unwindplan_regloc.IsAtAFAPlusOffset()) {
|
|
if (m_afa == LLDB_INVALID_ADDRESS)
|
|
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
|
|
|
|
int offset = unwindplan_regloc.GetOffset();
|
|
regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation;
|
|
regloc.location.target_memory_location = m_afa + offset;
|
|
m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
|
|
UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
|
|
"AFA 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;
|
|
DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr);
|
|
dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind(
|
|
unwindplan_registerkind);
|
|
Value cfa_val = Scalar(m_cfa);
|
|
cfa_val.SetValueType(Value::ValueType::LoadAddress);
|
|
Value result;
|
|
Status error;
|
|
if (dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr, 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 RegisterContextUnwind::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;
|
|
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();
|
|
if (ProcessSP process_sp = m_thread.GetProcess()) {
|
|
if (ABISP abi = process_sp->GetABI())
|
|
old_caller_pc_value = abi->FixCodeAddress(old_caller_pc_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
addr_t old_afa = m_afa;
|
|
|
|
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_backed_up_one);
|
|
|
|
if (active_row &&
|
|
active_row->GetCFAValue().GetValueType() !=
|
|
UnwindPlan::Row::FAValue::unspecified) {
|
|
addr_t new_cfa;
|
|
if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
|
|
active_row->GetCFAValue(), 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;
|
|
return false;
|
|
}
|
|
m_cfa = new_cfa;
|
|
|
|
ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
|
|
active_row->GetAFAValue(), m_afa);
|
|
|
|
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 (ProcessSP process_sp = m_thread.GetProcess()) {
|
|
if (ABISP abi = process_sp->GetABI())
|
|
new_caller_pc_value = abi->FixCodeAddress(new_caller_pc_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
m_afa = old_afa;
|
|
return false;
|
|
}
|
|
|
|
if (old_caller_pc_value == new_caller_pc_value &&
|
|
m_cfa == old_cfa &&
|
|
m_afa == old_afa) {
|
|
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;
|
|
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();
|
|
PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() {
|
|
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;
|
|
}
|
|
|
|
UnwindPlan::RowSP active_row =
|
|
m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
|
|
|
|
if (active_row &&
|
|
active_row->GetCFAValue().GetValueType() !=
|
|
UnwindPlan::Row::FAValue::unspecified) {
|
|
addr_t new_cfa;
|
|
if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
|
|
active_row->GetCFAValue(), 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;
|
|
}
|
|
|
|
ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
|
|
active_row->GetAFAValue(), m_afa);
|
|
|
|
m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
|
|
m_fallback_unwind_plan_sp.reset();
|
|
|
|
m_registers.clear();
|
|
|
|
m_cfa = new_cfa;
|
|
|
|
PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
|
|
|
|
UnwindLogMsg("switched unconditionally to the fallback unwindplan %s",
|
|
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(
|
|
lldb::UnwindPlanSP unwind_plan) {
|
|
if (unwind_plan->GetUnwindPlanForSignalTrap() != eLazyBoolYes) {
|
|
// Unwind plan does not indicate trap handler. Do nothing. We may
|
|
// already be flagged as trap handler flag due to the symbol being
|
|
// in the trap handler symbol list, and that should take precedence.
|
|
return;
|
|
} else if (m_frame_type != eNormalFrame) {
|
|
// If this is already a trap handler frame, nothing to do.
|
|
// If this is a skip or debug or invalid frame, don't override that.
|
|
return;
|
|
}
|
|
|
|
m_frame_type = eTrapHandlerFrame;
|
|
|
|
if (m_current_offset_backed_up_one != m_current_offset) {
|
|
// We backed up the pc by 1 to compute the symbol context, but
|
|
// now need to undo that because the pc of the trap handler
|
|
// frame may in fact be the first instruction of a signal return
|
|
// trampoline, rather than the instruction after a call. This
|
|
// happens on systems where the signal handler dispatch code, rather
|
|
// than calling the handler and being returned to, jumps to the
|
|
// handler after pushing the address of a return trampoline on the
|
|
// stack -- on these systems, when the handler returns, control will
|
|
// be transferred to the return trampoline, so that's the best
|
|
// symbol we can present in the callstack.
|
|
UnwindLogMsg("Resetting current offset and re-doing symbol lookup; "
|
|
"old symbol was %s",
|
|
GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
m_current_offset_backed_up_one = m_current_offset;
|
|
|
|
AddressRange addr_range;
|
|
m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx, &addr_range);
|
|
|
|
UnwindLogMsg("Symbol is now %s",
|
|
GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
|
|
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
Target *target = &process->GetTarget();
|
|
|
|
m_start_pc = addr_range.GetBaseAddress();
|
|
m_current_offset =
|
|
m_current_pc.GetLoadAddress(target) - m_start_pc.GetLoadAddress(target);
|
|
}
|
|
}
|
|
|
|
bool RegisterContextUnwind::ReadFrameAddress(
|
|
lldb::RegisterKind row_register_kind, UnwindPlan::Row::FAValue &fa,
|
|
addr_t &address) {
|
|
RegisterValue reg_value;
|
|
|
|
address = LLDB_INVALID_ADDRESS;
|
|
addr_t cfa_reg_contents;
|
|
|
|
switch (fa.GetValueType()) {
|
|
case UnwindPlan::Row::FAValue::isRegisterDereferenced: {
|
|
RegisterNumber cfa_reg(m_thread, row_register_kind,
|
|
fa.GetRegisterNumber());
|
|
if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
|
|
const RegisterInfo *reg_info =
|
|
GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB));
|
|
RegisterValue reg_value;
|
|
if (reg_info) {
|
|
Status error = ReadRegisterValueFromMemory(
|
|
reg_info, cfa_reg_contents, reg_info->byte_size, reg_value);
|
|
if (error.Success()) {
|
|
address = reg_value.GetAsUInt64();
|
|
if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
|
|
address = abi_sp->FixCodeAddress(address);
|
|
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, address);
|
|
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::FAValue::isRegisterPlusOffset: {
|
|
RegisterNumber cfa_reg(m_thread, row_register_kind,
|
|
fa.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;
|
|
}
|
|
address = cfa_reg_contents + fa.GetOffset();
|
|
UnwindLogMsg(
|
|
"CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64
|
|
", offset is %d",
|
|
address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
|
|
cfa_reg_contents, fa.GetOffset());
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case UnwindPlan::Row::FAValue::isDWARFExpression: {
|
|
ExecutionContext exe_ctx(m_thread.shared_from_this());
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(),
|
|
fa.GetDWARFExpressionLength(),
|
|
process->GetByteOrder(),
|
|
process->GetAddressByteSize());
|
|
ModuleSP opcode_ctx;
|
|
DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr);
|
|
dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind(
|
|
row_register_kind);
|
|
Value result;
|
|
Status error;
|
|
if (dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr, result,
|
|
&error)) {
|
|
address = result.GetScalar().ULongLong();
|
|
if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
|
|
address = abi_sp->FixCodeAddress(address);
|
|
|
|
UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64,
|
|
address);
|
|
return true;
|
|
}
|
|
UnwindLogMsg("Failed to set CFA value via DWARF expression: %s",
|
|
error.AsCString());
|
|
break;
|
|
}
|
|
case UnwindPlan::Row::FAValue::isRaSearch: {
|
|
Process &process = *m_thread.GetProcess();
|
|
lldb::addr_t return_address_hint = GetReturnAddressHint(fa.GetOffset());
|
|
if (return_address_hint == LLDB_INVALID_ADDRESS)
|
|
return false;
|
|
const unsigned max_iterations = 256;
|
|
for (unsigned i = 0; i < max_iterations; ++i) {
|
|
Status st;
|
|
lldb::addr_t candidate_addr =
|
|
return_address_hint + i * process.GetAddressByteSize();
|
|
lldb::addr_t candidate =
|
|
process.ReadPointerFromMemory(candidate_addr, st);
|
|
if (st.Fail()) {
|
|
UnwindLogMsg("Cannot read memory at 0x%" PRIx64 ": %s", candidate_addr,
|
|
st.AsCString());
|
|
return false;
|
|
}
|
|
Address addr;
|
|
uint32_t permissions;
|
|
if (process.GetLoadAddressPermissions(candidate, permissions) &&
|
|
permissions & lldb::ePermissionsExecutable) {
|
|
address = candidate_addr;
|
|
UnwindLogMsg("Heuristically found CFA: 0x%" PRIx64, address);
|
|
return true;
|
|
}
|
|
}
|
|
UnwindLogMsg("No suitable CFA found");
|
|
break;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
lldb::addr_t RegisterContextUnwind::GetReturnAddressHint(int32_t plan_offset) {
|
|
addr_t hint;
|
|
if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, hint))
|
|
return LLDB_INVALID_ADDRESS;
|
|
if (!m_sym_ctx.module_sp || !m_sym_ctx.symbol)
|
|
return LLDB_INVALID_ADDRESS;
|
|
|
|
hint += plan_offset;
|
|
|
|
if (auto next = GetNextFrame()) {
|
|
if (!next->m_sym_ctx.module_sp || !next->m_sym_ctx.symbol)
|
|
return LLDB_INVALID_ADDRESS;
|
|
if (auto expected_size =
|
|
next->m_sym_ctx.module_sp->GetSymbolFile()->GetParameterStackSize(
|
|
*next->m_sym_ctx.symbol))
|
|
hint += *expected_size;
|
|
else {
|
|
UnwindLogMsgVerbose("Could not retrieve parameter size: %s",
|
|
llvm::toString(expected_size.takeError()).c_str());
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
}
|
|
return hint;
|
|
}
|
|
|
|
// 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 RegisterContextUnwind::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 || regnum == LLDB_REGNUM_GENERIC_RA)) {
|
|
pc_register = true;
|
|
} else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
|
|
register_kind, regnum, eRegisterKindGeneric, generic_regnum) &&
|
|
(generic_regnum == LLDB_REGNUM_GENERIC_PC ||
|
|
generic_regnum == LLDB_REGNUM_GENERIC_RA)) {
|
|
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();
|
|
if (pc_register) {
|
|
if (ProcessSP process_sp = m_thread.GetProcess()) {
|
|
if (ABISP abi = process_sp->GetABI())
|
|
value = abi->FixCodeAddress(value);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RegisterContextUnwind::ReadGPRValue(const RegisterNumber ®num,
|
|
addr_t &value) {
|
|
return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(),
|
|
value);
|
|
}
|
|
|
|
// Find the value of a register in THIS frame
|
|
|
|
bool RegisterContextUnwind::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);
|
|
}
|
|
|
|
bool is_pc_regnum = false;
|
|
if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC ||
|
|
reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) {
|
|
is_pc_regnum = true;
|
|
}
|
|
|
|
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, is_pc_regnum))
|
|
return false;
|
|
|
|
bool result = ReadRegisterValueFromRegisterLocation(regloc, reg_info, value);
|
|
if (result) {
|
|
if (is_pc_regnum && value.GetType() == RegisterValue::eTypeUInt64) {
|
|
addr_t reg_value = value.GetAsUInt64(LLDB_INVALID_ADDRESS);
|
|
if (reg_value != LLDB_INVALID_ADDRESS) {
|
|
if(ProcessSP process_sp = m_thread.GetProcess()) {
|
|
if (ABISP abi = process_sp->GetABI())
|
|
value = abi->FixCodeAddress(reg_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool RegisterContextUnwind::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 RegisterContextUnwind::ReadAllRegisterValues(
|
|
lldb::WritableDataBufferSP &data_sp) {
|
|
return false;
|
|
}
|
|
|
|
// Don't need to implement this one
|
|
bool RegisterContextUnwind::WriteAllRegisterValues(
|
|
const lldb::DataBufferSP &data_sp) {
|
|
return false;
|
|
}
|
|
|
|
// Retrieve the pc value for THIS from
|
|
|
|
bool RegisterContextUnwind::GetCFA(addr_t &cfa) {
|
|
if (!IsValid()) {
|
|
return false;
|
|
}
|
|
if (m_cfa == LLDB_INVALID_ADDRESS) {
|
|
return false;
|
|
}
|
|
cfa = m_cfa;
|
|
return true;
|
|
}
|
|
|
|
RegisterContextUnwind::SharedPtr RegisterContextUnwind::GetNextFrame() const {
|
|
RegisterContextUnwind::SharedPtr regctx;
|
|
if (m_frame_number == 0)
|
|
return regctx;
|
|
return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1);
|
|
}
|
|
|
|
RegisterContextUnwind::SharedPtr RegisterContextUnwind::GetPrevFrame() const {
|
|
RegisterContextUnwind::SharedPtr regctx;
|
|
return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1);
|
|
}
|
|
|
|
// Retrieve the address of the start of the function of THIS frame
|
|
|
|
bool RegisterContextUnwind::GetStartPC(addr_t &start_pc) {
|
|
if (!IsValid())
|
|
return false;
|
|
|
|
if (!m_start_pc.IsValid()) {
|
|
bool read_successfully = ReadPC (start_pc);
|
|
if (read_successfully)
|
|
{
|
|
ProcessSP process_sp (m_thread.GetProcess());
|
|
if (process_sp)
|
|
{
|
|
ABI *abi = process_sp->GetABI().get();
|
|
if (abi)
|
|
start_pc = abi->FixCodeAddress(start_pc);
|
|
}
|
|
}
|
|
return read_successfully;
|
|
}
|
|
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 RegisterContextUnwind::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.
|
|
|
|
ProcessSP process_sp (m_thread.GetProcess());
|
|
if (process_sp)
|
|
{
|
|
ABI *abi = process_sp->GetABI().get();
|
|
if (abi)
|
|
pc = abi->FixCodeAddress(pc);
|
|
}
|
|
|
|
return !(m_all_registers_available == false &&
|
|
above_trap_handler == false && (pc == 0 || pc == 1));
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RegisterContextUnwind::UnwindLogMsg(const char *fmt, ...) {
|
|
Log *log = GetLog(LLDBLog::Unwind);
|
|
if (!log)
|
|
return;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
llvm::SmallString<0> logmsg;
|
|
if (VASprintf(logmsg, fmt, args)) {
|
|
LLDB_LOGF(log, "%*sth%d/fr%u %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "",
|
|
m_thread.GetIndexID(), m_frame_number, logmsg.c_str());
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
void RegisterContextUnwind::UnwindLogMsgVerbose(const char *fmt, ...) {
|
|
Log *log = GetLog(LLDBLog::Unwind);
|
|
if (!log || !log->GetVerbose())
|
|
return;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
llvm::SmallString<0> logmsg;
|
|
if (VASprintf(logmsg, fmt, args)) {
|
|
LLDB_LOGF(log, "%*sth%d/fr%u %s",
|
|
m_frame_number < 100 ? m_frame_number : 100, "",
|
|
m_thread.GetIndexID(), m_frame_number, logmsg.c_str());
|
|
}
|
|
va_end(args);
|
|
}
|