forked from OSchip/llvm-project
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:
parent
287f0449a2
commit
60f0bd4944
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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 ®loc, bool check_next_frame);
|
||||
SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc);
|
||||
|
||||
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.
|
||||
|
|
|
@ -262,14 +262,24 @@ UnwindLLDB::GetRegisterContextForFrameNum (uint32_t frame_num)
|
|||
}
|
||||
|
||||
bool
|
||||
UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num)
|
||||
UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, 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--;
|
||||
}
|
||||
|
|
|
@ -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 ®loc, uint32_t starting_frame_num);
|
||||
SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_or_return_address_reg);
|
||||
|
||||
|
||||
private:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue