llvm-project/lldb/source/Symbol/FuncUnwinders.cpp
Jason Molenda 5c45c541a2 Various unwinder work.
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
2014-12-21 10:44:54 +00:00

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;
}