Add a new capability to RegisterContextLLDB: To recognize when the

Full UnwindPlan is trying to do an impossible unwind; in that case
invalidate the Full UnwindPlan and replace it with the architecture
default unwind plan.

This is a scenario that happens occasionally with arm unwinds in
particular; the instruction analysis based full unwindplan can
mis-parse the functions and the stack walk stops prematurely.  Now
we can do a simpleminded frame-chain walk to find the caller frame
and continue the unwind.  It's not ideal but given the complicated
nature of analyzing the arm functions, and the lack of eh_frame
information on iOS, it is a distinct improvement and fixes some
long-standing problems with the unwinder on that platform.  

This is fixing <rdar://problem/12091421>.  I may re-use this
invalidate feature in the future if I can identify other cases where
the full unwindplan's unwind information is clearly incorrect.

This checkin also includes some cleanup for the volatile register
definition in the arm ABI plugin for <rdar://problem/10652166> 
although work remains to be done for that bug.

llvm-svn: 166757
This commit is contained in:
Jason Molenda 2012-10-26 06:08:58 +00:00
parent 287f0449a2
commit 60f0bd4944
14 changed files with 486 additions and 355 deletions

View File

@ -70,6 +70,13 @@ public:
return m_range.ContainsFileAddress (addr);
}
// When we're doing an unwind using the UnwindPlanAtNonCallSite and we find an
// impossible unwind condition, we know that the UnwindPlan is invalid. Calling
// this method on the FuncUnwinder will tell it to replace that UnwindPlan with
// the architectural default UnwindPlan so hopefully our stack walk will get past
// this frame.
void
InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& Thread);
private:
UnwindTable& m_unwind_table;

View File

@ -349,7 +349,9 @@ public:
m_plan_valid_address_range (),
m_register_kind (reg_kind),
m_return_addr_register (LLDB_INVALID_REGNUM),
m_source_name ()
m_source_name (),
m_plan_is_sourced_from_compiler (eLazyBoolCalculate),
m_plan_is_valid_at_all_instruction_locations (eLazyBoolCalculate)
{
}
@ -432,6 +434,36 @@ public:
void
SetSourceName (const char *);
// Was this UnwindPlan emitted by a compiler?
lldb_private::LazyBool
GetSourcedFromCompiler () const
{
return m_plan_is_sourced_from_compiler;
}
// Was this UnwindPlan emitted by a compiler?
void
SetSourcedFromCompiler (lldb_private::LazyBool from_compiler)
{
m_plan_is_sourced_from_compiler = from_compiler;
}
// Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites,
// e.g. for exception handling.
lldb_private::LazyBool
GetUnwindPlanValidAtAllInstructions () const
{
return m_plan_is_valid_at_all_instruction_locations;
}
// Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites,
// e.g. for exception handling.
void
SetUnwindPlanValidAtAllInstructions (lldb_private::LazyBool valid_at_all_insn)
{
m_plan_is_valid_at_all_instruction_locations = valid_at_all_insn;
}
int
GetRowCount () const;
@ -458,6 +490,8 @@ private:
uint32_t m_return_addr_register; // The register that has the return address for the caller frame
// e.g. the lr on arm
lldb_private::ConstString m_source_name; // for logging, where this UnwindPlan originated from
lldb_private::LazyBool m_plan_is_sourced_from_compiler;
lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations;
}; // class UnwindPlan
} // namespace lldb_private

View File

@ -628,6 +628,8 @@ ABIMacOSX_arm::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan)
// All other registers are the same.
unwind_plan.SetSourceName ("arm at-func-entry default");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
return true;
}
@ -651,9 +653,30 @@ ABIMacOSX_arm::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan)
unwind_plan.AppendRow (row);
unwind_plan.SetSourceName ("arm-apple-ios default unwind plan");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
return true;
}
// ARMv7 on iOS general purpose reg rules:
// r0-r3 not preserved (used for argument passing)
// r4-r6 preserved
// r7 preserved (frame pointer)
// r8 preserved
// r9 not preserved (usable as volatile scratch register with iOS 3.x and later)
// r10-r11 preserved
// r12 not presrved
// r13 preserved (stack pointer)
// r14 not preserved (link register)
// r15 preserved (pc)
// cpsr not preserved (different rules for different bits)
// ARMv7 on iOS floating point rules:
// d0-d7 not preserved (aka s0-s15, q0-q3)
// d8-d15 preserved (aka s16-s31, q4-q7)
// d16-d31 not preserved (aka q8-q15)
bool
ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info)
{
@ -691,28 +714,28 @@ ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info)
switch (name[1])
{
case '0':
return name[2] == '\0'; // d0
return name[2] == '\0'; // d0 is volatile
case '1':
switch (name[2])
{
case '\0':
return true; // d1;
return true; // d1 is volatile
case '6':
case '7':
case '8':
case '9':
return name[3] == '\0'; // d16 - d19
return name[3] == '\0'; // d16 - d19 are volatile
default:
break;
}
break;
case '2':
switch (name[2])
{
case '\0':
return true; // d2;
return true; // d2 is volatile
case '0':
case '1':
case '2':
@ -723,7 +746,7 @@ ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info)
case '7':
case '8':
case '9':
return name[3] == '\0'; // d20 - d29
return name[3] == '\0'; // d20 - d29 are volatile
default:
break;
}
@ -733,10 +756,10 @@ ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info)
switch (name[2])
{
case '\0':
return true; // d3;
return true; // d3 is volatile
case '0':
case '1':
return name[3] == '\0'; // d30 - d31
return name[3] == '\0'; // d30 - d31 are volatile
default:
break;
}
@ -744,7 +767,61 @@ ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info)
case '5':
case '6':
case '7':
return name[2] == '\0'; // d4 - d7
return name[2] == '\0'; // d4 - d7 are volatile
default:
break;
}
}
else if (name[0] == 's')
{
switch (name[1])
{
case '0':
return name[2] == '\0'; // s0 is volatile
case '1':
switch (name[2])
{
case '\0':
return true; // s1 is volatile
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
return name[3] == '\0'; // s10 - s15 are volatile
default:
break;
}
break;
case '2':
switch (name[2])
{
case '\0':
return true; // s2 is volatile
default:
break;
}
break;
case '3':
switch (name[2])
{
case '\0':
return true; // s3 is volatile
default:
break;
}
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return name[2] == '\0'; // s4 - s9 are volatile
default:
break;

View File

@ -894,6 +894,7 @@ ABIMacOSX_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan)
row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);
unwind_plan.AppendRow (row);
unwind_plan.SetSourceName ("i386 at-func-entry default");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
return true;
}
@ -919,6 +920,8 @@ ABIMacOSX_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan)
unwind_plan.AppendRow (row);
unwind_plan.SetSourceName ("i386 default unwind plan");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
return true;
}

View File

@ -1088,6 +1088,7 @@ ABISysV_x86_64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan)
row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false);
unwind_plan.AppendRow (row);
unwind_plan.SetSourceName ("x86_64 at-func-entry default");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
return true;
}
@ -1139,6 +1140,8 @@ ABISysV_x86_64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan)
unwind_plan.AppendRow (row);
unwind_plan.SetSourceName ("x86_64 default unwind plan");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
return true;
}

View File

@ -13613,9 +13613,7 @@ EmulateInstructionARM::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan)
// All other registers are the same.
unwind_plan.SetSourceName ("EmulateInstructionARM");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes);
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -59,7 +59,7 @@ public:
virtual bool
WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value);
virtual bool
ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
@ -96,14 +96,14 @@ private:
friend class UnwindLLDB;
// Indicates whether this frame is frame zero -- the currently
// executing frame -- or not.
// executing frame -- or not.
bool
IsFrameZero () const;
void
void
InitializeZerothFrame ();
void
void
InitializeNonZerothFrame();
SharedPtr
@ -112,7 +112,7 @@ private:
SharedPtr
GetPrevFrame () const;
// A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've got one bogus frame at frame #1.
// A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've got one bogus frame at frame #1.
// There is a good chance we'll get back on track if we follow the frame pointer chain (or whatever is appropriate
// on this ABI) so we allow one invalid frame to be in the stack. Ideally we'll mark this frame specially at some
// point and indicate to the user that the unwinder had a hiccup. Often when this happens we will miss a frame of
@ -124,32 +124,30 @@ private:
// Or a frame "below" this one saved it, i.e. a function called by this one, preserved a register that this
// function didn't modify/use.
//
// The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register
// The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register
// being queried mid-stack. Instead of floating frame 0's contents of that register up the stack (which may
// or may not be the value of that reg when the function was executing), we won't return any value.
//
// If a non-volatile register (a "preserved" register) is requested mid-stack and no frames "below" the requested
// stack have saved the register anywhere, it is safe to assume that frame 0's register values are still the same
// as the requesting frame's.
//
// NB this function takes a "check_next_frame" boolean which indicates whether it should call back to the
// containing UnwindLLDB object to iterate the search down the stack (true) or if this call should look for
// a register save for that reg in the current frame only (false). Allows UnwindLLDB to iterate through the
// RegisterContextLLDB's instead of using recursion to find saved register values.
bool
SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, bool check_next_frame);
SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc);
bool
ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
const lldb_private::RegisterInfo *reg_info,
lldb_private::RegisterValue &value);
bool
WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc,
const lldb_private::RegisterInfo *reg_info,
const lldb_private::RegisterValue &value);
// Get the contents of a general purpose (address-size) register for this frame
void
InvalidateFullUnwindPlan ();
// Get the contents of a general purpose (address-size) register for this frame
// (usually retrieved from the next frame)
bool
ReadGPRValue (int register_kind, uint32_t regnum, lldb::addr_t &value);
@ -160,8 +158,14 @@ private:
lldb::UnwindPlanSP
GetFullUnwindPlanForFrame ();
void
UnwindLogMsg (const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void
UnwindLogMsgVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
lldb_private::Thread& m_thread;
///
// The following tell us how to retrieve the CALLER's register values (ie the "previous" frame, aka the frame above)
// i.e. where THIS frame saved them
@ -182,7 +186,7 @@ private:
int m_current_offset_backed_up_one; // how far into the function we've executed; -1 if unknown
// 0 if no instructions have been executed yet.
// On architectures where the return address on the stack points
// to the instruction after the CALL, this value will have 1
// to the instruction after the CALL, this value will have 1
// subtracted from it. Else a function that ends in a CALL will
// have an offset pointing into the next function's address range.
// m_current_pc has the actual address of the "current" pc.

View File

@ -262,14 +262,24 @@ UnwindLLDB::GetRegisterContextForFrameNum (uint32_t frame_num)
}
bool
UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num)
UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num, bool pc_or_return_address_reg)
{
int64_t frame_num = starting_frame_num;
if (frame_num >= m_frames.size())
return false;
// Never interrogate more than one level while looking for the saved pc value. If the value
// isn't saved by frame_num, none of the frames lower on the stack will have a useful value.
if (pc_or_return_address_reg)
{
if (m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc))
return true;
else
return false;
}
while (frame_num >= 0)
{
if (m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc, false))
if (m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc))
return true;
frame_num--;
}

View File

@ -80,7 +80,7 @@ protected:
// Iterate over the RegisterContextLLDB's in our m_frames vector, look for the first one that
// has a saved location for this reg.
bool
SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num);
SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation &regloc, uint32_t starting_frame_num, bool pc_or_return_address_reg);
private:

View File

@ -738,6 +738,8 @@ loopnext:
}
unwind_plan.SetSourceName ("assembly insn profiling");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes);
return true;
}
@ -822,6 +824,9 @@ AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_
row.reset(newrow);
unwind_plan.SetPlanValidAddressRange (func);
unwind_plan.SetSourceName ("fast unwind assembly profiling");
unwind_plan.SetSourcedFromCompiler (eLazyBoolNo);
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
return true;
}

View File

@ -371,11 +371,18 @@ DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t offset, Address startaddr, Unwi
{
unwind_plan.SetSourceName ("eh_frame CFI");
cie_offset = current_entry + 4 - cie_offset;
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
}
else
{
unwind_plan.SetSourceName ("DWARF CFI");
// In theory the debug_frame info should be valid at all call sites
// ("asynchronous unwind info" as it is sometimes called) but in practice
// gcc et al all emit call frame info for the prologue and call sites, but
// not for the epilogue or all the other locations during the function reliably.
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
}
unwind_plan.SetSourcedFromCompiler (eLazyBoolYes);
const CIE *cie = GetCIE (cie_offset);
assert (cie != NULL);

View File

@ -236,3 +236,12 @@ FuncUnwinders::GetFunctionStartAddress () const
return m_range.GetBaseAddress();
}
void
FuncUnwinders::InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& thread)
{
UnwindPlanSP arch_default = GetUnwindPlanArchitectureDefault (thread);
if (arch_default && m_tried_unwind_at_call_site)
{
m_unwind_plan_call_site_sp = arch_default;
}
}

View File

@ -153,7 +153,7 @@ void
UnwindPlan::Row::Clear ()
{
m_offset = 0;
m_cfa_reg_num = 0;
m_cfa_reg_num = LLDB_INVALID_REGNUM;
m_cfa_offset = 0;
m_register_locations.clear();
}
@ -189,7 +189,7 @@ UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread,
UnwindPlan::Row::Row() :
m_offset(0),
m_cfa_reg_num(0),
m_cfa_reg_num(LLDB_INVALID_REGNUM),
m_cfa_offset(0),
m_register_locations()
{