Files
clang-p2996/lldb/source/Symbol/FuncUnwinders.cpp
Jason Molenda cabd1b71c7 I'm not thrilled with how I structured this but RegisterContextLLDB
needs to use the current pc and current offset in two ways:  To 
determine which function we are currently executing, and the decide
how much of that function has executed so far.  For the former use,
we need to back up the saved pc value by one byte if we're going to
use the correct function's unwind information -- we may be executing
a CALL instruction at the end of a function and the following instruction
belongs to a new function, or we may be looking at unwind information
which only covers the call instruction and not the subsequent instruction.

But when we're talking about deciding which row of an UnwindPlan to
execute, we want to use the actual byte offset in the function, not the
byte offset - 1.

Right now RegisterContextLLDB is tracking both the "real" offset and
an "offset minus one" and different parts of the class have to know 
which one to use and they need to be updated/set in tandem.  I want
to revisit this at some point.

The second change made in looking up eh_frame information; it was
formerly done by looking for the start address of the function we
are currently executing.  But it is possible to have unwind information
for a function which only covers a small section of the function's
address range.  In which case looking up by the start pc value may not
find the eh_frame FDE.

The hand-written _sigtramp() unwind info on Mac OS X, which covers
exactly one instruction in the middle of the function, happens to
trigger both of these issues.

I still need to get the UnwindPlan runner to handle arbitrary dwarf
expressions in the FDE but there's a good chance it will be easy to
reuse the DWARFExpression class to do this.

llvm-svn: 118882
2010-11-12 05:23:10 +00:00

151 lines
4.4 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/lldb-private.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Address.h"
#include "lldb/Symbol/UnwindTable.h"
#include "lldb/Utility/UnwindAssemblyProfiler.h"
#include "lldb/Utility/ArchDefaultUnwindPlan.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
FuncUnwinders::FuncUnwinders (UnwindTable& unwind_table, UnwindAssemblyProfiler *assembly_profiler, AddressRange range) :
m_unwind_table(unwind_table),
m_assembly_profiler(assembly_profiler),
m_range(range),
m_unwind_at_call_site(NULL),
m_unwind_at_non_call_site(NULL),
m_fast_unwind(NULL),
m_arch_default_unwind(NULL),
m_first_non_prologue_insn() { }
FuncUnwinders::~FuncUnwinders ()
{
if (m_unwind_at_call_site)
delete m_unwind_at_call_site;
if (m_unwind_at_non_call_site)
delete m_unwind_at_non_call_site;
if (m_fast_unwind)
delete m_fast_unwind;
if (m_arch_default_unwind)
delete m_arch_default_unwind;
}
UnwindPlan*
FuncUnwinders::GetUnwindPlanAtCallSite (int current_offset)
{
if (m_unwind_at_call_site != NULL)
return m_unwind_at_call_site;
if (!m_range.GetBaseAddress().IsValid())
return NULL;
// We have cases (e.g. with _sigtramp on Mac OS X) where the hand-written eh_frame unwind info for a
// function does not cover the entire range of the function and so the FDE only lists a subset of the
// address range. If we try to look up the unwind info by the starting address of the function
// (i.e. m_range.GetBaseAddress()) we may not find the eh_frame FDE. We need to use the actual byte offset
// into the function when looking it up.
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();
UnwindPlan *up = NULL;
if (eh_frame)
{
up = new UnwindPlan;
if (!eh_frame->GetUnwindPlan (current_pc, *up))
{
delete up;
return NULL;
}
}
if (!up)
return NULL;
m_unwind_at_call_site = up;
return m_unwind_at_call_site;
}
UnwindPlan*
FuncUnwinders::GetUnwindPlanAtNonCallSite (Thread& thread)
{
if (m_unwind_at_non_call_site != NULL)
return m_unwind_at_non_call_site;
UnwindPlan *up = new UnwindPlan;
if (!m_assembly_profiler->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *up))
{
delete up;
return NULL;
}
m_unwind_at_non_call_site = up;
return m_unwind_at_non_call_site;
}
UnwindPlan*
FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread)
{
if (m_fast_unwind != NULL)
return m_fast_unwind;
UnwindPlan *up = new UnwindPlan;
if (!m_assembly_profiler->GetFastUnwindPlan (m_range, thread, *up))
{
delete up;
return NULL;
}
m_fast_unwind = up;
return m_fast_unwind;
}
UnwindPlan*
FuncUnwinders::GetUnwindPlanArchitectureDefault (Thread& thread)
{
if (m_arch_default_unwind != NULL)
return m_arch_default_unwind;
Address current_pc;
Target *target = thread.CalculateTarget();
if (target)
{
ArchSpec arch = target->GetArchitecture ();
ArchDefaultUnwindPlan *arch_default = ArchDefaultUnwindPlan::FindPlugin (arch);
if (arch_default)
{
m_arch_default_unwind = arch_default->GetArchDefaultUnwindPlan (thread, current_pc);
}
}
return m_arch_default_unwind;
}
Address&
FuncUnwinders::GetFirstNonPrologueInsn (Target& target)
{
if (m_first_non_prologue_insn.IsValid())
return m_first_non_prologue_insn;
m_assembly_profiler->FirstNonPrologueInsn (m_range, target, NULL, m_first_non_prologue_insn);
return m_first_non_prologue_insn;
}
const Address&
FuncUnwinders::GetFunctionStartAddress () const
{
return m_range.GetBaseAddress();
}