Skip to content

Commit

Permalink
Fix several issues around .ARM.exidx section handling
Browse files Browse the repository at this point in the history
* Use .ARM.exidx as a fallback unwind plan for non-call site when the
  instruction emulation based unwind failed.
* Work around an old compiler issue where the compiler isn't sort the
  entries in .ARM.exidx based on their address.
* Fix unwind info parsing when the virtual file address >= 0x80000000
* Fix bug in unwind info parsing when neither lr nor pc is explicitly
  restored.

Differential revision: http://reviews.llvm.org/D13380

llvm-svn: 249119
  • Loading branch information
Tamas Berghammer committed Oct 2, 2015
1 parent 8c205d5 commit f5ead56
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 46 deletions.
13 changes: 13 additions & 0 deletions lldb/include/lldb/Symbol/ArmUnwindInfo.h
Expand Up @@ -43,6 +43,18 @@ class ArmUnwindInfo
GetUnwindPlan(Target &target, const Address& addr, UnwindPlan& unwind_plan);

private:
struct ArmExidxEntry
{
ArmExidxEntry(uint32_t f, lldb::addr_t a, uint32_t d);

bool
operator<(const ArmExidxEntry& other) const;

uint32_t file_address;
lldb::addr_t address;
uint32_t data;
};

const uint8_t*
GetExceptionHandlingTableEntry(const Address& addr);

Expand All @@ -57,6 +69,7 @@ class ArmUnwindInfo
lldb::SectionSP m_arm_extab_sp; // .ARM.extab section
DataExtractor m_arm_exidx_data; // .ARM.exidx section data
DataExtractor m_arm_extab_data; // .ARM.extab section data
std::vector<ArmExidxEntry> m_exidx_entries;
};

} // namespace lldb_private
Expand Down
20 changes: 10 additions & 10 deletions lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
Expand Up @@ -881,12 +881,12 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
// then the architecture default plan and for hand written assembly code it is often
// written in a way that it valid at all location what helps in the most common
// cases when the instruction emulation fails.
UnwindPlanSP eh_frame_unwind_plan = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one);
if (eh_frame_unwind_plan &&
eh_frame_unwind_plan.get() != unwind_plan_sp.get() &&
eh_frame_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
if (call_site_unwind_plan &&
call_site_unwind_plan.get() != unwind_plan_sp.get() &&
call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
{
m_fallback_unwind_plan_sp = eh_frame_unwind_plan;
m_fallback_unwind_plan_sp = call_site_unwind_plan;
}
else
{
Expand Down Expand Up @@ -926,12 +926,12 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
// more reliable even on non call sites then the architecture default plan and for hand
// written assembly code it is often written in a way that it valid at all location what
// helps in the most common cases when the instruction emulation fails.
UnwindPlanSP eh_frame_unwind_plan = func_unwinders_sp->GetEHFrameUnwindPlan (process->GetTarget(), m_current_offset_backed_up_one);
if (eh_frame_unwind_plan &&
eh_frame_unwind_plan.get() != unwind_plan_sp.get() &&
eh_frame_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(), m_current_offset_backed_up_one);
if (call_site_unwind_plan &&
call_site_unwind_plan.get() != unwind_plan_sp.get() &&
call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName())
{
m_fallback_unwind_plan_sp = eh_frame_unwind_plan;
m_fallback_unwind_plan_sp = call_site_unwind_plan;
}
else
{
Expand Down
84 changes: 48 additions & 36 deletions lldb/source/Symbol/ArmUnwindInfo.cpp
Expand Up @@ -30,14 +30,26 @@
using namespace lldb;
using namespace lldb_private;

namespace
// Converts a prel31 avlue to lldb::addr_t with sign extension
static addr_t
Prel31ToAddr(uint32_t prel31)
{
struct ArmExidxEntry
{
uint32_t address;
uint32_t data;
};
};
addr_t res = prel31;
if (prel31 & (1<<30))
res |= 0xffffffff80000000ULL;
return res;
}

ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a, uint32_t d) :
file_address(f), address(a), data(d)
{
}

bool
ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry& other) const
{
return address < other.address;
}

ArmUnwindInfo::ArmUnwindInfo(const ObjectFile& objfile,
SectionSP& arm_exidx,
Expand All @@ -48,6 +60,23 @@ ArmUnwindInfo::ArmUnwindInfo(const ObjectFile& objfile,
{
objfile.ReadSectionData(arm_exidx.get(), m_arm_exidx_data);
objfile.ReadSectionData(arm_extab.get(), m_arm_extab_data);

addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress();

offset_t offset = 0;
while (m_arm_exidx_data.ValidOffset(offset))
{
lldb::addr_t file_addr = exidx_base_addr + offset;
lldb::addr_t addr = exidx_base_addr +
(addr_t)offset +
Prel31ToAddr(m_arm_exidx_data.GetU32(&offset));
uint32_t data = m_arm_exidx_data.GetU32(&offset);
m_exidx_entries.emplace_back(file_addr, addr, data);
}

// Sort the entries in the exidx section. The entries should be sorted inside the section but
// some old compiler isn't sorted them.
std::sort(m_exidx_entries.begin(), m_exidx_entries.end());
}

ArmUnwindInfo::~ArmUnwindInfo()
Expand Down Expand Up @@ -85,7 +114,7 @@ ArmUnwindInfo::GetULEB128(const uint32_t* data, uint16_t& offset, uint16_t max_o
bool
ArmUnwindInfo::GetUnwindPlan(Target &target, const Address& addr, UnwindPlan& unwind_plan)
{
const uint32_t* data = (const uint32_t*)GetExceptionHandlingTableEntry(addr.GetFileAddress());
const uint32_t* data = (const uint32_t*)GetExceptionHandlingTableEntry(addr);
if (data == nullptr)
return false; // No unwind information for the function

Expand Down Expand Up @@ -382,6 +411,8 @@ ArmUnwindInfo::GetUnwindPlan(Target &target, const Address& addr, UnwindPlan& un
UnwindPlan::Row::RegisterLocation lr_location;
if (row->GetRegisterInfo(dwarf_lr, lr_location))
row->SetRegisterInfo(dwarf_pc, lr_location);
else
row->SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, false);
}

unwind_plan.AppendRow(row);
Expand All @@ -396,38 +427,19 @@ ArmUnwindInfo::GetUnwindPlan(Target &target, const Address& addr, UnwindPlan& un
const uint8_t*
ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address& addr)
{
uint32_t file_addr = addr.GetFileAddress();
uint32_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress();

const ArmExidxEntry* exidx_start = (const ArmExidxEntry*)m_arm_exidx_data.GetDataStart();
uint32_t bs_start = 0, bs_end = m_arm_exidx_data.GetByteSize() / sizeof(ArmExidxEntry);
while (bs_start + 1 < bs_end)
{
uint32_t mid = (bs_start + bs_end) / 2;
uint32_t mid_addr = exidx_base_addr + exidx_start[mid].address + mid * sizeof(ArmExidxEntry);
mid_addr &= 0x7fffffff;
if (mid_addr > file_addr)
bs_end = mid;
else
bs_start = mid;
}

uint32_t exidx_addr = exidx_base_addr +
exidx_start[bs_start].address +
bs_start * sizeof(ArmExidxEntry);
exidx_addr &= 0x7fffffff;
if (exidx_addr > file_addr)
auto it = std::upper_bound(m_exidx_entries.begin(),
m_exidx_entries.end(),
ArmExidxEntry{0, addr.GetFileAddress(), 0});
if (it == m_exidx_entries.begin())
return nullptr;
--it;

if (exidx_start[bs_start].data == 0x1)
if (it->data == 0x1)
return nullptr; // EXIDX_CANTUNWIND

if (exidx_start[bs_start].data & 0x80000000)
return (const uint8_t*)&exidx_start[bs_start].data;
if (it->data & 0x80000000)
return (const uint8_t*)&it->data;

uint32_t data_file_addr = exidx_base_addr +
8 * bs_start + 4 +
exidx_start[bs_start].data;
data_file_addr &= 0x7fffffff;
addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(it->data);
return m_arm_extab_data.GetDataStart() + (data_file_addr - m_arm_extab_sp->GetFileAddress());
}

0 comments on commit f5ead56

Please sign in to comment.