When unwinding from the first frame, try to ask the remote debugserver

if this is a mapped/executable region of memory.  If it isn't, we've jumped
through a bad pointer and we know how to unwind the stack correctly based
on the ABI.  

Previously I had 0x0 special cased but if you jumped to 0x2 on x86_64 one
frame would be skipped because the unwinder would try using the x86_64 
ArchDefaultUnwindPlan which relied on the rbp.

Fixes <rdar://problem/10508291>

llvm-svn: 146477
This commit is contained in:
Jason Molenda 2011-12-13 05:39:38 +00:00
parent f43e681ed7
commit cb349ee19c
4 changed files with 147 additions and 47 deletions

View File

@ -592,6 +592,18 @@ tuples tp return are:
// a hex encoded string value that
// contains an error string
If the address requested is not in a mapped region (e.g. we've jumped through
a NULL pointer and are at 0x0) currently lldb expects to get back the size
of the unmapped region -- that is, the distance to the next valid region.
For instance, with a Mac OS X process which has nothing mapped in the first
4GB of its address space, if we're asking about address 0x2,
qMemoryRegionInfo:2
start:2;size:fffffffe;
The lack of 'permissions:' indicates that none of read/write/execute are valid
for this region.
//----------------------------------------------------------------------
// Stop reply packet extensions
//

View File

@ -1177,10 +1177,18 @@ class MemoryRegionInfo
{
public:
typedef Range<lldb::addr_t, lldb::addr_t> RangeType;
enum OptionalBool {
eDontKnow = -1,
eNo = 0,
eYes = 1
};
MemoryRegionInfo () :
m_range (),
m_permissions (0)
m_read (eDontKnow),
m_write (eDontKnow),
m_execute (eDontKnow)
{
}
@ -1198,7 +1206,7 @@ public:
Clear()
{
m_range.Clear();
m_permissions = 0;
m_read = m_write = m_execute = eDontKnow;
}
const RangeType &
@ -1206,42 +1214,48 @@ public:
{
return m_range;
}
// Pass in a uint32_t permissions with one or more lldb::Permissions
// enumeration values logical OR'ed together.
bool
TestPermissions (uint32_t permissions) const
OptionalBool
GetReadable () const
{
return m_permissions.AllSet(permissions);
return m_read;
}
const Flags &
GetPermissions () const
OptionalBool
GetWritable () const
{
return m_permissions;
return m_write;
}
OptionalBool
GetExecutable () const
{
return m_execute;
}
void
SetPermissions (uint32_t permissions)
SetReadable (OptionalBool val)
{
m_permissions.Reset(permissions);
}
void
AddPermissions (uint32_t permissions)
{
m_permissions.Set (permissions);
m_read = val;
}
void
RemovePermissions (uint32_t permissions)
SetWritable (OptionalBool val)
{
m_permissions.Clear (permissions);
m_write = val;
}
void
SetExecutable (OptionalBool val)
{
m_execute = val;
}
protected:
RangeType m_range;
Flags m_permissions; // Uses lldb::Permissions enumeration values logical OR'ed together
OptionalBool m_read;
OptionalBool m_write;
OptionalBool m_execute;
};
//----------------------------------------------------------------------
@ -2558,17 +2572,56 @@ public:
error.SetErrorString ("Process::GetMemoryRegionInfo() not supported");
return error;
}
virtual uint32_t
GetLoadAddressPermissions (lldb::addr_t load_addr)
//------------------------------------------------------------------
/// Attempt to get the attributes for a region of memory in the process.
///
/// It may be possible for the remote debug server to inspect attributes
/// for a region of memory in the process, such as whether there is a
/// valid page of memory at a given address or whether that page is
/// readable/writable/executable by the process.
///
/// @param[in] load_addr
/// The address of interest in the process.
///
/// @param[out] permissions
/// If this call returns successfully, this bitmask will have
/// its Permissions bits set to indicate whether the region is
/// readable/writable/executable. If this call fails, the
/// bitmask values are undefined.
///
/// @return
/// Returns true if it was able to determine the attributes of the
/// memory region. False if not.
//------------------------------------------------------------------
virtual bool
GetLoadAddressPermissions (lldb::addr_t load_addr, uint32_t &permissions)
{
MemoryRegionInfo range_info;
permissions = 0;
Error error (GetMemoryRegionInfo (load_addr, range_info));
if (error.Success())
return range_info.GetPermissions().Get();
return 0;
if (!error.Success())
return false;
if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow
|| range_info.GetWritable() == MemoryRegionInfo::eDontKnow
|| range_info.GetExecutable() == MemoryRegionInfo::eDontKnow)
{
return false;
}
if (range_info.GetReadable() == MemoryRegionInfo::eYes)
permissions |= lldb::ePermissionsReadable;
if (range_info.GetWritable() == MemoryRegionInfo::eYes)
permissions |= lldb::ePermissionsWritable;
if (range_info.GetExecutable() == MemoryRegionInfo::eYes)
permissions |= lldb::ePermissionsExecutable;
return true;
}
//------------------------------------------------------------------
/// Determines whether executing JIT-compiled code in this process
/// is possible.

View File

@ -562,8 +562,7 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
UnwindPlanSP unwind_plan_sp;
LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
UnwindPlanSP arch_default_unwind_plan_sp;
ABI *abi = m_thread.GetProcess().GetABI().get();
if (abi)
{
@ -584,14 +583,22 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
// If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) so the pc is 0x0
// in the zeroth frame, we need to use the "unwind at first instruction" arch default UnwindPlan
if (behaves_like_zeroth_frame
&& m_current_pc.IsValid()
&& m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()) == 0)
// Also, if this Process can report on memory region attributes, any non-executable region means
// we jumped through a bad function pointer - handle the same way as 0x0.
if (behaves_like_zeroth_frame && m_current_pc.IsValid())
{
unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
m_frame_type = eNormalFrame;
return unwind_plan_sp;
uint32_t permissions;
addr_t current_pc_addr = m_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget());
if (current_pc_addr == 0
|| (m_thread.GetProcess().GetLoadAddressPermissions(current_pc_addr, permissions)
&& (permissions & ePermissionsExecutable) == 0))
{
unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp);
m_frame_type = eNormalFrame;
return unwind_plan_sp;
}
}
// No Module fm_current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()or the current pc, try using the architecture default unwind.

View File

@ -1108,6 +1108,7 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr,
std::string value;
addr_t addr_value;
bool success = true;
bool saw_permissions = false;
while (success && response.GetNameColonValue(name, value))
{
if (name.compare ("start") == 0)
@ -1122,14 +1123,33 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr,
if (success)
region_info.GetRange().SetByteSize (addr_value);
}
else if (name.compare ("permissions") == 0)
else if (name.compare ("permissions") == 0 && region_info.GetRange().IsValid())
{
if (value.find('r') != std::string::npos)
region_info.AddPermissions (ePermissionsReadable);
if (value.find('w') != std::string::npos)
region_info.AddPermissions (ePermissionsWritable);
if (value.find('x') != std::string::npos)
region_info.AddPermissions (ePermissionsExecutable);
saw_permissions = true;
if (region_info.GetRange().Contains (addr))
{
if (value.find('r') != std::string::npos)
region_info.SetReadable (MemoryRegionInfo::eYes);
else
region_info.SetReadable (MemoryRegionInfo::eNo);
if (value.find('w') != std::string::npos)
region_info.SetWritable (MemoryRegionInfo::eYes);
else
region_info.SetWritable (MemoryRegionInfo::eNo);
if (value.find('x') != std::string::npos)
region_info.SetExecutable (MemoryRegionInfo::eYes);
else
region_info.SetExecutable (MemoryRegionInfo::eNo);
}
else
{
// The reported region does not contain this address -- we're looking at an unmapped page
region_info.SetReadable (MemoryRegionInfo::eNo);
region_info.SetWritable (MemoryRegionInfo::eNo);
region_info.SetExecutable (MemoryRegionInfo::eNo);
}
}
else if (name.compare ("error") == 0)
{
@ -1141,6 +1161,14 @@ GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr,
error.SetErrorString(value.c_str());
}
}
// We got a valid address range back but no permissions -- which means this is an unmapped page
if (region_info.GetRange().IsValid() && saw_permissions == false)
{
region_info.SetReadable (MemoryRegionInfo::eNo);
region_info.SetWritable (MemoryRegionInfo::eNo);
region_info.SetExecutable (MemoryRegionInfo::eNo);
}
}
else
{