
Most of the changes are to the FuncUnwinders class -- as we've added more types of unwind information, the way this class was written was making it a mess to maintain. Instead of trying to keep one "non-call site" unwind plan and one "call site" unwind plan, track all the different types of unwind plans we can possibly retrieve for each function and have the call-site/non-call-site accessor methods retrieve those. Add a real "fast unwind plan" for x86_64 / i386 -- when doing an unwind through a function, this only has to read the first 4 bytes to tell if the function has a standard prologue sequence. If so, we can use the architecture default unwind plan to backtrace through this function. If we try to retrieve the save location for other registers later on, a real unwind plan will be used. This one is just for doing fast backtraces. Change the compact unwind plan importer to fill in the valid address range it is valid for. Compact unwind, in theory, may have multiple entries for a single function. The FuncUnwinders rewrite includes the start of supporting this correctly. In practice compact unwind encodings are used for the entire range of the function today -- in fact, sometimes the same encoding is used for multiple functions that have the same unwind rules. But I want to handle a single function that has multiple different compact unwind UnwindPlans eventually. llvm-svn: 224689
361 lines
12 KiB
C++
361 lines
12 KiB
C++
//===-- FuncUnwinders.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/Core/AddressRange.h"
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Symbol/FuncUnwinders.h"
|
|
#include "lldb/Symbol/DWARFCallFrameInfo.h"
|
|
#include "lldb/Symbol/CompactUnwindInfo.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/UnwindPlan.h"
|
|
#include "lldb/Symbol/UnwindTable.h"
|
|
#include "lldb/Target/ABI.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/UnwindAssembly.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
//------------------------------------------------
|
|
/// constructor
|
|
//------------------------------------------------
|
|
|
|
FuncUnwinders::FuncUnwinders (UnwindTable& unwind_table, AddressRange range) :
|
|
m_unwind_table (unwind_table),
|
|
m_range (range),
|
|
m_mutex (Mutex::eMutexTypeRecursive),
|
|
m_unwind_plan_assembly_sp (),
|
|
m_unwind_plan_eh_frame_sp (),
|
|
m_unwind_plan_eh_frame_augmented_sp (),
|
|
m_unwind_plan_compact_unwind (),
|
|
m_unwind_plan_fast_sp (),
|
|
m_unwind_plan_arch_default_sp (),
|
|
m_unwind_plan_arch_default_at_func_entry_sp (),
|
|
m_tried_unwind_plan_assembly (false),
|
|
m_tried_unwind_plan_eh_frame (false),
|
|
m_tried_unwind_plan_eh_frame_augmented (false),
|
|
m_tried_unwind_plan_compact_unwind (false),
|
|
m_tried_unwind_fast (false),
|
|
m_tried_unwind_arch_default (false),
|
|
m_tried_unwind_arch_default_at_func_entry (false),
|
|
m_first_non_prologue_insn ()
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------
|
|
/// destructor
|
|
//------------------------------------------------
|
|
|
|
FuncUnwinders::~FuncUnwinders ()
|
|
{
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetUnwindPlanAtCallSite (Target &target, int current_offset)
|
|
{
|
|
Mutex::Locker locker (m_mutex);
|
|
|
|
UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, current_offset);
|
|
if (unwind_plan_sp.get() == nullptr)
|
|
{
|
|
unwind_plan_sp = GetCompactUnwindUnwindPlan (target, current_offset);
|
|
}
|
|
|
|
return unwind_plan_sp;
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetCompactUnwindUnwindPlan (Target &target, int current_offset)
|
|
{
|
|
if (m_unwind_plan_compact_unwind.size() > 0)
|
|
return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact unwind plans for one func
|
|
if (m_tried_unwind_plan_compact_unwind)
|
|
return UnwindPlanSP();
|
|
|
|
Mutex::Locker lock (m_mutex);
|
|
m_tried_unwind_plan_compact_unwind = true;
|
|
if (m_range.GetBaseAddress().IsValid())
|
|
{
|
|
Address current_pc (m_range.GetBaseAddress ());
|
|
if (current_offset != -1)
|
|
current_pc.SetOffset (current_pc.GetOffset() + current_offset);
|
|
CompactUnwindInfo *compact_unwind = m_unwind_table.GetCompactUnwindInfo();
|
|
if (compact_unwind)
|
|
{
|
|
UnwindPlanSP unwind_plan_sp (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (compact_unwind->GetUnwindPlan (target, current_pc, *unwind_plan_sp))
|
|
{
|
|
m_unwind_plan_compact_unwind.push_back (unwind_plan_sp);
|
|
return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact unwind plans for one func
|
|
}
|
|
}
|
|
}
|
|
return UnwindPlanSP();
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetEHFrameUnwindPlan (Target &target, int current_offset)
|
|
{
|
|
if (m_unwind_plan_eh_frame_sp.get() || m_tried_unwind_plan_eh_frame)
|
|
return m_unwind_plan_eh_frame_sp;
|
|
|
|
Mutex::Locker lock (m_mutex);
|
|
m_tried_unwind_plan_eh_frame = true;
|
|
if (m_range.GetBaseAddress().IsValid())
|
|
{
|
|
Address current_pc (m_range.GetBaseAddress ());
|
|
if (current_offset != -1)
|
|
current_pc.SetOffset (current_pc.GetOffset() + current_offset);
|
|
DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo();
|
|
if (eh_frame)
|
|
{
|
|
m_unwind_plan_eh_frame_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_eh_frame_sp))
|
|
m_unwind_plan_eh_frame_sp.reset();
|
|
}
|
|
}
|
|
return m_unwind_plan_eh_frame_sp;
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetEHFrameAugmentedUnwindPlan (Target &target, Thread &thread, int current_offset)
|
|
{
|
|
if (m_unwind_plan_eh_frame_augmented_sp.get() || m_tried_unwind_plan_eh_frame_augmented)
|
|
return m_unwind_plan_eh_frame_augmented_sp;
|
|
|
|
// Only supported on x86 architectures where we get eh_frame from the compiler that describes
|
|
// the prologue instructions perfectly, and sometimes the epilogue instructions too.
|
|
if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386
|
|
&& target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64
|
|
&& target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h)
|
|
{
|
|
m_tried_unwind_plan_eh_frame_augmented = true;
|
|
return m_unwind_plan_eh_frame_augmented_sp;
|
|
}
|
|
|
|
Mutex::Locker lock (m_mutex);
|
|
m_tried_unwind_plan_eh_frame_augmented = true;
|
|
|
|
if (m_range.GetBaseAddress().IsValid())
|
|
{
|
|
Address current_pc (m_range.GetBaseAddress ());
|
|
if (current_offset != -1)
|
|
current_pc.SetOffset (current_pc.GetOffset() + current_offset);
|
|
DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo();
|
|
if (eh_frame)
|
|
{
|
|
m_unwind_plan_eh_frame_augmented_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_eh_frame_augmented_sp))
|
|
{
|
|
m_unwind_plan_eh_frame_augmented_sp.reset();
|
|
}
|
|
else
|
|
{
|
|
// Augment the eh_frame instructions with epilogue descriptions if necessary so the
|
|
// UnwindPlan can be used at any instruction in the function.
|
|
|
|
UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler());
|
|
if (assembly_profiler_sp)
|
|
{
|
|
if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite (m_range, thread, *m_unwind_plan_eh_frame_augmented_sp))
|
|
{
|
|
m_unwind_plan_eh_frame_augmented_sp.reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_unwind_plan_eh_frame_augmented_sp.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return m_unwind_plan_eh_frame_augmented_sp;
|
|
}
|
|
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetAssemblyUnwindPlan (Target &target, Thread &thread, int current_offset)
|
|
{
|
|
if (m_unwind_plan_assembly_sp.get() || m_tried_unwind_plan_assembly)
|
|
return m_unwind_plan_assembly_sp;
|
|
|
|
Mutex::Locker lock (m_mutex);
|
|
m_tried_unwind_plan_assembly = true;
|
|
|
|
UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler());
|
|
if (assembly_profiler_sp)
|
|
{
|
|
m_unwind_plan_assembly_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!assembly_profiler_sp->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *m_unwind_plan_assembly_sp))
|
|
{
|
|
m_unwind_plan_assembly_sp.reset();
|
|
}
|
|
}
|
|
return m_unwind_plan_assembly_sp;
|
|
}
|
|
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetUnwindPlanAtNonCallSite (Target& target, Thread& thread, int current_offset)
|
|
{
|
|
UnwindPlanSP non_call_site_unwindplan_sp = GetEHFrameAugmentedUnwindPlan (target, thread, current_offset);
|
|
if (non_call_site_unwindplan_sp.get() == nullptr)
|
|
{
|
|
non_call_site_unwindplan_sp = GetAssemblyUnwindPlan (target, thread, current_offset);
|
|
}
|
|
return non_call_site_unwindplan_sp;
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread)
|
|
{
|
|
if (m_unwind_plan_fast_sp.get() || m_tried_unwind_fast)
|
|
return m_unwind_plan_fast_sp;
|
|
|
|
Mutex::Locker locker (m_mutex);
|
|
m_tried_unwind_fast = true;
|
|
|
|
UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler());
|
|
if (assembly_profiler_sp)
|
|
{
|
|
m_unwind_plan_fast_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!assembly_profiler_sp->GetFastUnwindPlan (m_range, thread, *m_unwind_plan_fast_sp))
|
|
{
|
|
m_unwind_plan_fast_sp.reset();
|
|
}
|
|
}
|
|
return m_unwind_plan_fast_sp;
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetUnwindPlanArchitectureDefault (Thread& thread)
|
|
{
|
|
if (m_unwind_plan_arch_default_sp.get() || m_tried_unwind_arch_default)
|
|
return m_unwind_plan_arch_default_sp;
|
|
|
|
Mutex::Locker locker (m_mutex);
|
|
m_tried_unwind_arch_default = true;
|
|
|
|
Address current_pc;
|
|
ProcessSP process_sp (thread.CalculateProcess());
|
|
if (process_sp)
|
|
{
|
|
ABI *abi = process_sp->GetABI().get();
|
|
if (abi)
|
|
{
|
|
m_unwind_plan_arch_default_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!abi->CreateDefaultUnwindPlan(*m_unwind_plan_arch_default_sp))
|
|
{
|
|
m_unwind_plan_arch_default_sp.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_unwind_plan_arch_default_sp;
|
|
}
|
|
|
|
UnwindPlanSP
|
|
FuncUnwinders::GetUnwindPlanArchitectureDefaultAtFunctionEntry (Thread& thread)
|
|
{
|
|
if (m_unwind_plan_arch_default_at_func_entry_sp.get() || m_tried_unwind_arch_default_at_func_entry)
|
|
return m_unwind_plan_arch_default_at_func_entry_sp;
|
|
|
|
Mutex::Locker locker (m_mutex);
|
|
m_tried_unwind_arch_default_at_func_entry = true;
|
|
|
|
Address current_pc;
|
|
ProcessSP process_sp (thread.CalculateProcess());
|
|
if (process_sp)
|
|
{
|
|
ABI *abi = process_sp->GetABI().get();
|
|
if (abi)
|
|
{
|
|
m_unwind_plan_arch_default_at_func_entry_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
|
if (!abi->CreateFunctionEntryUnwindPlan(*m_unwind_plan_arch_default_at_func_entry_sp))
|
|
{
|
|
m_unwind_plan_arch_default_at_func_entry_sp.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_unwind_plan_arch_default_at_func_entry_sp;
|
|
}
|
|
|
|
|
|
Address&
|
|
FuncUnwinders::GetFirstNonPrologueInsn (Target& target)
|
|
{
|
|
if (m_first_non_prologue_insn.IsValid())
|
|
return m_first_non_prologue_insn;
|
|
|
|
Mutex::Locker locker (m_mutex);
|
|
ExecutionContext exe_ctx (target.shared_from_this(), false);
|
|
UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler());
|
|
if (assembly_profiler_sp)
|
|
assembly_profiler_sp->FirstNonPrologueInsn (m_range, exe_ctx, m_first_non_prologue_insn);
|
|
return m_first_non_prologue_insn;
|
|
}
|
|
|
|
const Address&
|
|
FuncUnwinders::GetFunctionStartAddress () const
|
|
{
|
|
return m_range.GetBaseAddress();
|
|
}
|
|
|
|
lldb::UnwindAssemblySP
|
|
FuncUnwinders::GetUnwindAssemblyProfiler ()
|
|
{
|
|
UnwindAssemblySP assembly_profiler_sp;
|
|
ArchSpec arch;
|
|
if (m_unwind_table.GetArchitecture (arch))
|
|
{
|
|
assembly_profiler_sp = UnwindAssembly::FindPlugin (arch);
|
|
}
|
|
return assembly_profiler_sp;
|
|
}
|
|
|
|
Address
|
|
FuncUnwinders::GetLSDAAddress (Target &target)
|
|
{
|
|
Address lsda_addr;
|
|
|
|
UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, -1);
|
|
if (unwind_plan_sp.get() == nullptr)
|
|
{
|
|
unwind_plan_sp = GetCompactUnwindUnwindPlan (target, -1);
|
|
}
|
|
if (unwind_plan_sp.get() && unwind_plan_sp->GetLSDAAddress().IsValid())
|
|
{
|
|
lsda_addr = unwind_plan_sp->GetLSDAAddress();
|
|
}
|
|
return lsda_addr;
|
|
}
|
|
|
|
|
|
Address
|
|
FuncUnwinders::GetPersonalityRoutinePtrAddress (Target &target)
|
|
{
|
|
Address personality_addr;
|
|
|
|
UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, -1);
|
|
if (unwind_plan_sp.get() == nullptr)
|
|
{
|
|
unwind_plan_sp = GetCompactUnwindUnwindPlan (target, -1);
|
|
}
|
|
if (unwind_plan_sp.get() && unwind_plan_sp->GetPersonalityFunctionPtr().IsValid())
|
|
{
|
|
personality_addr = unwind_plan_sp->GetPersonalityFunctionPtr();
|
|
}
|
|
|
|
return personality_addr;
|
|
}
|