forked from OSchip/llvm-project
Add debug_frame section support
Summary: This is a beefed-up version of D33504, which adds support for dwarf 4 debug_frame section format. The main difference here is that the decision whether to use eh_frame or debug_frame is done on a per-function basis instead of per-object file. This is necessary because one module can contain both sections (for example, the start files added by the linker will typically pull in eh_frame), but we want to be able to access both, for maximum information. I also add unit test for parsing various CFI formats (eh_frame, debug_frame v3 and debug_frame v4). Reviewers: jasonmolenda, clayborg Subscribers: mgorny, aprantl, abidh, lldb-commits, tatyana-krasnukha Differential Revision: https://reviews.llvm.org/D34613 llvm-svn: 306397
This commit is contained in:
parent
7caff0e9e1
commit
cdda23eb7c
|
@ -37,7 +37,7 @@ public:
|
|||
DWARFCallFrameInfo(ObjectFile &objfile, lldb::SectionSP §ion,
|
||||
lldb::RegisterKind reg_kind, bool is_eh_frame);
|
||||
|
||||
~DWARFCallFrameInfo();
|
||||
~DWARFCallFrameInfo() = default;
|
||||
|
||||
// Locate an AddressRange that includes the provided Address in this
|
||||
// object's eh_frame/debug_info
|
||||
|
@ -74,12 +74,20 @@ public:
|
|||
|
||||
private:
|
||||
enum { CFI_AUG_MAX_SIZE = 8, CFI_HEADER_SIZE = 8 };
|
||||
enum CFIVersion {
|
||||
CFI_VERSION1 = 1, // DWARF v.2
|
||||
CFI_VERSION3 = 3, // DWARF v.3
|
||||
CFI_VERSION4 = 4 // DWARF v.4, v.5
|
||||
};
|
||||
|
||||
struct CIE {
|
||||
dw_offset_t cie_offset;
|
||||
uint8_t version;
|
||||
char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very
|
||||
// short.
|
||||
uint8_t address_size = sizeof(uint32_t); // The size of a target address.
|
||||
uint8_t segment_size = 0; // The size of a segment selector.
|
||||
|
||||
uint32_t code_align;
|
||||
int32_t data_align;
|
||||
uint32_t return_addr_reg_num;
|
||||
|
|
|
@ -99,6 +99,13 @@ public:
|
|||
Thread &thread,
|
||||
int current_offset);
|
||||
|
||||
lldb::UnwindPlanSP GetDebugFrameUnwindPlan(Target &target,
|
||||
int current_offset);
|
||||
|
||||
lldb::UnwindPlanSP GetDebugFrameAugmentedUnwindPlan(Target &target,
|
||||
Thread &thread,
|
||||
int current_offset);
|
||||
|
||||
lldb::UnwindPlanSP GetCompactUnwindUnwindPlan(Target &target,
|
||||
int current_offset);
|
||||
|
||||
|
@ -126,10 +133,12 @@ private:
|
|||
|
||||
lldb::UnwindPlanSP m_unwind_plan_assembly_sp;
|
||||
lldb::UnwindPlanSP m_unwind_plan_eh_frame_sp;
|
||||
lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp; // augmented by
|
||||
// assembly inspection
|
||||
// so it's valid
|
||||
// everywhere
|
||||
lldb::UnwindPlanSP m_unwind_plan_debug_frame_sp;
|
||||
|
||||
// augmented by assembly inspection so it's valid everywhere
|
||||
lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp;
|
||||
lldb::UnwindPlanSP m_unwind_plan_debug_frame_augmented_sp;
|
||||
|
||||
std::vector<lldb::UnwindPlanSP> m_unwind_plan_compact_unwind;
|
||||
lldb::UnwindPlanSP m_unwind_plan_arm_unwind_sp;
|
||||
lldb::UnwindPlanSP m_unwind_plan_fast_sp;
|
||||
|
@ -139,7 +148,9 @@ private:
|
|||
// Fetching the UnwindPlans can be expensive - if we've already attempted
|
||||
// to get one & failed, don't try again.
|
||||
bool m_tried_unwind_plan_assembly : 1, m_tried_unwind_plan_eh_frame : 1,
|
||||
m_tried_unwind_plan_debug_frame : 1,
|
||||
m_tried_unwind_plan_eh_frame_augmented : 1,
|
||||
m_tried_unwind_plan_debug_frame_augmented : 1,
|
||||
m_tried_unwind_plan_compact_unwind : 1,
|
||||
m_tried_unwind_plan_arm_unwind : 1, m_tried_unwind_fast : 1,
|
||||
m_tried_unwind_arch_default : 1,
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
~UnwindTable();
|
||||
|
||||
lldb_private::DWARFCallFrameInfo *GetEHFrameInfo();
|
||||
lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo();
|
||||
|
||||
lldb_private::CompactUnwindInfo *GetCompactUnwindInfo();
|
||||
|
||||
|
@ -58,6 +59,8 @@ private:
|
|||
void Dump(Stream &s);
|
||||
|
||||
void Initialize();
|
||||
llvm::Optional<AddressRange> GetAddressRange(const Address &addr,
|
||||
SymbolContext &sc);
|
||||
|
||||
typedef std::map<lldb::addr_t, lldb::FuncUnwindersSP> collection;
|
||||
typedef collection::iterator iterator;
|
||||
|
@ -70,6 +73,7 @@ private:
|
|||
std::mutex m_mutex;
|
||||
|
||||
std::unique_ptr<DWARFCallFrameInfo> m_eh_frame_up;
|
||||
std::unique_ptr<DWARFCallFrameInfo> m_debug_frame_up;
|
||||
std::unique_ptr<CompactUnwindInfo> m_compact_unwind_up;
|
||||
std::unique_ptr<ArmUnwindInfo> m_arm_unwind_up;
|
||||
|
||||
|
|
|
@ -3426,6 +3426,23 @@ protected:
|
|||
result.GetOutputStream().Printf("\n");
|
||||
}
|
||||
|
||||
if (UnwindPlanSP plan_sp =
|
||||
func_unwinders_sp->GetDebugFrameUnwindPlan(*target, 0)) {
|
||||
result.GetOutputStream().Printf("debug_frame UnwindPlan:\n");
|
||||
plan_sp->Dump(result.GetOutputStream(), thread.get(),
|
||||
LLDB_INVALID_ADDRESS);
|
||||
result.GetOutputStream().Printf("\n");
|
||||
}
|
||||
|
||||
if (UnwindPlanSP plan_sp =
|
||||
func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target,
|
||||
*thread, 0)) {
|
||||
result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n");
|
||||
plan_sp->Dump(result.GetOutputStream(), thread.get(),
|
||||
LLDB_INVALID_ADDRESS);
|
||||
result.GetOutputStream().Printf("\n");
|
||||
}
|
||||
|
||||
UnwindPlanSP arm_unwind_sp =
|
||||
func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0);
|
||||
if (arm_unwind_sp) {
|
||||
|
|
|
@ -161,8 +161,6 @@ DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile,
|
|||
m_fde_index(), m_fde_index_initialized(false),
|
||||
m_is_eh_frame(is_eh_frame) {}
|
||||
|
||||
DWARFCallFrameInfo::~DWARFCallFrameInfo() {}
|
||||
|
||||
bool DWARFCallFrameInfo::GetUnwindPlan(Address addr, UnwindPlan &unwind_plan) {
|
||||
FDEEntryMap::Entry fde_entry;
|
||||
|
||||
|
@ -276,6 +274,12 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
|
|||
// cie.cieID = cieID;
|
||||
cie_sp->ptr_encoding = DW_EH_PE_absptr; // default
|
||||
cie_sp->version = m_cfi_data.GetU8(&offset);
|
||||
if (cie_sp->version > CFI_VERSION4) {
|
||||
Host::SystemLog(Host::eSystemLogError,
|
||||
"CIE parse error: CFI version %d is not supported\n",
|
||||
cie_sp->version);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (i = 0; i < CFI_AUG_MAX_SIZE; ++i) {
|
||||
cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset);
|
||||
|
@ -294,11 +298,23 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
|
|||
"CIE parse error: CIE augmentation string was too large "
|
||||
"for the fixed sized buffer of %d bytes.\n",
|
||||
CFI_AUG_MAX_SIZE);
|
||||
return cie_sp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// m_cfi_data uses address size from target architecture of the process
|
||||
// may ignore these fields?
|
||||
if (!m_is_eh_frame && cie_sp->version >= CFI_VERSION4) {
|
||||
cie_sp->address_size = m_cfi_data.GetU8(&offset);
|
||||
cie_sp->segment_size = m_cfi_data.GetU8(&offset);
|
||||
}
|
||||
|
||||
cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset);
|
||||
cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset);
|
||||
cie_sp->return_addr_reg_num = m_cfi_data.GetU8(&offset);
|
||||
|
||||
cie_sp->return_addr_reg_num =
|
||||
!m_is_eh_frame && cie_sp->version >= CFI_VERSION3
|
||||
? static_cast<uint32_t>(m_cfi_data.GetULEB128(&offset))
|
||||
: m_cfi_data.GetU8(&offset);
|
||||
|
||||
if (cie_sp->augmentation[0]) {
|
||||
// Get the length of the eh_frame augmentation data
|
||||
|
@ -461,11 +477,33 @@ void DWARFCallFrameInfo::GetFDEIndex() {
|
|||
m_fde_index_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// An FDE entry contains CIE_pointer in debug_frame in same place as cie_id
|
||||
// in eh_frame. CIE_pointer is an offset into the .debug_frame section.
|
||||
// So, variable cie_offset should be equal to cie_id for debug_frame.
|
||||
// FDE entries with cie_id == 0 shouldn't be ignored for it.
|
||||
if ((cie_id == 0 && m_is_eh_frame) || cie_id == UINT32_MAX || len == 0) {
|
||||
auto cie_sp = ParseCIE(current_entry);
|
||||
if (!cie_sp) {
|
||||
// Cannot parse, the reason is already logged
|
||||
m_fde_index.Clear();
|
||||
m_fde_index_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_cie_map[current_entry] = std::move(cie_sp);
|
||||
offset = next_entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_is_eh_frame)
|
||||
cie_offset = cie_id;
|
||||
|
||||
if (cie_offset > m_cfi_data.GetByteSize()) {
|
||||
Host::SystemLog(
|
||||
Host::eSystemLogError,
|
||||
"error: Invalid cie offset of 0x%x found in cie/fde at 0x%x\n",
|
||||
cie_offset, current_entry);
|
||||
Host::SystemLog(Host::eSystemLogError,
|
||||
"error: Invalid cie offset of 0x%x "
|
||||
"found in cie/fde at 0x%x\n",
|
||||
cie_offset, current_entry);
|
||||
// Don't trust anything in this eh_frame section if we find blatantly
|
||||
// invalid data.
|
||||
m_fde_index.Clear();
|
||||
|
@ -473,12 +511,6 @@ void DWARFCallFrameInfo::GetFDEIndex() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cie_id == 0 || cie_id == UINT32_MAX || len == 0) {
|
||||
m_cie_map[current_entry] = ParseCIE(current_entry);
|
||||
offset = next_entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
const CIE *cie = GetCIE(cie_offset);
|
||||
if (cie) {
|
||||
const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
|
||||
|
@ -531,7 +563,8 @@ bool DWARFCallFrameInfo::FDEToUnwindPlan(dw_offset_t dwarf_offset,
|
|||
cie_offset = m_cfi_data.GetU32(&offset);
|
||||
}
|
||||
|
||||
assert(cie_offset != 0 && cie_offset != UINT32_MAX);
|
||||
// FDE entries with zeroth cie_offset may occur for debug_frame.
|
||||
assert(!(m_is_eh_frame && 0 == cie_offset) && cie_offset != UINT32_MAX);
|
||||
|
||||
// Translate the CIE_id from the eh_frame format, which
|
||||
// is relative to the FDE offset, into a __eh_frame section
|
||||
|
|
|
@ -39,7 +39,9 @@ FuncUnwinders::FuncUnwinders(UnwindTable &unwind_table, AddressRange range)
|
|||
m_unwind_plan_arch_default_sp(),
|
||||
m_unwind_plan_arch_default_at_func_entry_sp(),
|
||||
m_tried_unwind_plan_assembly(false), m_tried_unwind_plan_eh_frame(false),
|
||||
m_tried_unwind_plan_debug_frame(false),
|
||||
m_tried_unwind_plan_eh_frame_augmented(false),
|
||||
m_tried_unwind_plan_debug_frame_augmented(false),
|
||||
m_tried_unwind_plan_compact_unwind(false),
|
||||
m_tried_unwind_plan_arm_unwind(false), m_tried_unwind_fast(false),
|
||||
m_tried_unwind_arch_default(false),
|
||||
|
@ -56,17 +58,14 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtCallSite(Target &target,
|
|||
int current_offset) {
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
|
||||
UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan(target, current_offset);
|
||||
if (unwind_plan_sp)
|
||||
return unwind_plan_sp;
|
||||
|
||||
unwind_plan_sp = GetCompactUnwindUnwindPlan(target, current_offset);
|
||||
if (unwind_plan_sp)
|
||||
return unwind_plan_sp;
|
||||
|
||||
unwind_plan_sp = GetArmUnwindUnwindPlan(target, current_offset);
|
||||
if (unwind_plan_sp)
|
||||
return unwind_plan_sp;
|
||||
if (UnwindPlanSP plan_sp = GetEHFrameUnwindPlan(target, current_offset))
|
||||
return plan_sp;
|
||||
if (UnwindPlanSP plan_sp = GetDebugFrameUnwindPlan(target, current_offset))
|
||||
return plan_sp;
|
||||
if (UnwindPlanSP plan_sp = GetCompactUnwindUnwindPlan(target, current_offset))
|
||||
return plan_sp;
|
||||
if (UnwindPlanSP plan_sp = GetArmUnwindUnwindPlan(target, current_offset))
|
||||
return plan_sp;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -121,6 +120,29 @@ UnwindPlanSP FuncUnwinders::GetEHFrameUnwindPlan(Target &target,
|
|||
return m_unwind_plan_eh_frame_sp;
|
||||
}
|
||||
|
||||
UnwindPlanSP FuncUnwinders::GetDebugFrameUnwindPlan(Target &target,
|
||||
int current_offset) {
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
if (m_unwind_plan_debug_frame_sp || m_tried_unwind_plan_debug_frame)
|
||||
return m_unwind_plan_debug_frame_sp;
|
||||
|
||||
m_tried_unwind_plan_debug_frame = true;
|
||||
if (m_range.GetBaseAddress().IsValid()) {
|
||||
Address current_pc(m_range.GetBaseAddress());
|
||||
if (current_offset != -1)
|
||||
current_pc.SetOffset(current_pc.GetOffset() + current_offset);
|
||||
DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo();
|
||||
if (debug_frame) {
|
||||
m_unwind_plan_debug_frame_sp.reset(
|
||||
new UnwindPlan(lldb::eRegisterKindGeneric));
|
||||
if (!debug_frame->GetUnwindPlan(current_pc,
|
||||
*m_unwind_plan_debug_frame_sp))
|
||||
m_unwind_plan_debug_frame_sp.reset();
|
||||
}
|
||||
}
|
||||
return m_unwind_plan_debug_frame_sp;
|
||||
}
|
||||
|
||||
UnwindPlanSP FuncUnwinders::GetArmUnwindUnwindPlan(Target &target,
|
||||
int current_offset) {
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
|
@ -187,6 +209,48 @@ UnwindPlanSP FuncUnwinders::GetEHFrameAugmentedUnwindPlan(Target &target,
|
|||
return m_unwind_plan_eh_frame_augmented_sp;
|
||||
}
|
||||
|
||||
UnwindPlanSP
|
||||
FuncUnwinders::GetDebugFrameAugmentedUnwindPlan(Target &target, Thread &thread,
|
||||
int current_offset) {
|
||||
std::lock_guard<std::recursive_mutex> guard(m_mutex);
|
||||
if (m_unwind_plan_debug_frame_augmented_sp.get() ||
|
||||
m_tried_unwind_plan_debug_frame_augmented)
|
||||
return m_unwind_plan_debug_frame_augmented_sp;
|
||||
|
||||
// Only supported on x86 architectures where we get debug_frame from the
|
||||
// compiler that describes the prologue instructions perfectly, and sometimes
|
||||
// the epilogue instructions too.
|
||||
if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
|
||||
target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
|
||||
target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
|
||||
m_tried_unwind_plan_debug_frame_augmented = true;
|
||||
return m_unwind_plan_debug_frame_augmented_sp;
|
||||
}
|
||||
|
||||
m_tried_unwind_plan_debug_frame_augmented = true;
|
||||
|
||||
UnwindPlanSP debug_frame_plan =
|
||||
GetDebugFrameUnwindPlan(target, current_offset);
|
||||
if (!debug_frame_plan)
|
||||
return m_unwind_plan_debug_frame_augmented_sp;
|
||||
|
||||
m_unwind_plan_debug_frame_augmented_sp.reset(
|
||||
new UnwindPlan(*debug_frame_plan));
|
||||
|
||||
// Augment the debug_frame instructions with epilogue descriptions if
|
||||
// necessary so the UnwindPlan can be used at any instruction in the function.
|
||||
|
||||
UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
|
||||
if (assembly_profiler_sp) {
|
||||
if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite(
|
||||
m_range, thread, *m_unwind_plan_debug_frame_augmented_sp)) {
|
||||
m_unwind_plan_debug_frame_augmented_sp.reset();
|
||||
}
|
||||
} else
|
||||
m_unwind_plan_debug_frame_augmented_sp.reset();
|
||||
return m_unwind_plan_debug_frame_augmented_sp;
|
||||
}
|
||||
|
||||
UnwindPlanSP FuncUnwinders::GetAssemblyUnwindPlan(Target &target,
|
||||
Thread &thread,
|
||||
int current_offset) {
|
||||
|
@ -248,6 +312,8 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
|
|||
Thread &thread,
|
||||
int current_offset) {
|
||||
UnwindPlanSP eh_frame_sp = GetEHFrameUnwindPlan(target, current_offset);
|
||||
if (!eh_frame_sp)
|
||||
eh_frame_sp = GetDebugFrameUnwindPlan(target, current_offset);
|
||||
UnwindPlanSP arch_default_at_entry_sp =
|
||||
GetUnwindPlanArchitectureDefaultAtFunctionEntry(thread);
|
||||
UnwindPlanSP arch_default_sp = GetUnwindPlanArchitectureDefault(thread);
|
||||
|
@ -255,28 +321,22 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
|
|||
GetAssemblyUnwindPlan(target, thread, current_offset);
|
||||
|
||||
// This point of this code is to detect when a function is using a
|
||||
// non-standard ABI, and the eh_frame
|
||||
// correctly describes that alternate ABI. This is addressing a specific
|
||||
// situation on x86_64 linux
|
||||
// systems where one function in a library pushes a value on the stack and
|
||||
// jumps to another function.
|
||||
// So using an assembly instruction based unwind will not work when you're in
|
||||
// the second function -
|
||||
// the stack has been modified in a non-ABI way. But we have eh_frame that
|
||||
// correctly describes how to
|
||||
// unwind from this location. So we're looking to see if the initial pc
|
||||
// register save location from
|
||||
// the eh_frame is different from the assembly unwind, the arch default
|
||||
// unwind, and the arch default at
|
||||
// initial function entry.
|
||||
// non-standard ABI, and the eh_frame correctly describes that alternate ABI.
|
||||
// This is addressing a specific situation on x86_64 linux systems where one
|
||||
// function in a library pushes a value on the stack and jumps to another
|
||||
// function. So using an assembly instruction based unwind will not work when
|
||||
// you're in the second function - the stack has been modified in a non-ABI
|
||||
// way. But we have eh_frame that correctly describes how to unwind from this
|
||||
// location. So we're looking to see if the initial pc register save location
|
||||
// from the eh_frame is different from the assembly unwind, the arch default
|
||||
// unwind, and the arch default at initial function entry.
|
||||
//
|
||||
// We may have eh_frame that describes the entire function -- or we may have
|
||||
// eh_frame that only describes
|
||||
// the unwind after the prologue has executed -- so we need to check both the
|
||||
// arch default (once the prologue
|
||||
// has executed) and the arch default at initial function entry. And we may
|
||||
// be running on a target where
|
||||
// we have only some of the assembly/arch default unwind plans available.
|
||||
// eh_frame that only describes the unwind after the prologue has executed --
|
||||
// so we need to check both the arch default (once the prologue has executed)
|
||||
// and the arch default at initial function entry. And we may be running on a
|
||||
// target where we have only some of the assembly/arch default unwind plans
|
||||
// available.
|
||||
|
||||
if (CompareUnwindPlansForIdenticalInitialPCLocation(
|
||||
thread, eh_frame_sp, arch_default_at_entry_sp) == eLazyBoolNo &&
|
||||
|
@ -287,11 +347,12 @@ UnwindPlanSP FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target,
|
|||
return eh_frame_sp;
|
||||
}
|
||||
|
||||
UnwindPlanSP eh_frame_augmented_sp =
|
||||
GetEHFrameAugmentedUnwindPlan(target, thread, current_offset);
|
||||
if (eh_frame_augmented_sp) {
|
||||
return eh_frame_augmented_sp;
|
||||
}
|
||||
if (UnwindPlanSP plan_sp =
|
||||
GetEHFrameAugmentedUnwindPlan(target, thread, current_offset))
|
||||
return plan_sp;
|
||||
if (UnwindPlanSP plan_sp =
|
||||
GetDebugFrameAugmentedUnwindPlan(target, thread, current_offset))
|
||||
return plan_sp;
|
||||
|
||||
return assembly_sp;
|
||||
}
|
||||
|
|
|
@ -44,38 +44,64 @@ void UnwindTable::Initialize() {
|
|||
|
||||
if (m_initialized) // check again once we've acquired the lock
|
||||
return;
|
||||
m_initialized = true;
|
||||
|
||||
SectionList *sl = m_object_file.GetSectionList();
|
||||
if (sl) {
|
||||
SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
|
||||
if (sect.get()) {
|
||||
m_eh_frame_up.reset(new DWARFCallFrameInfo(m_object_file, sect,
|
||||
eRegisterKindEHFrame, true));
|
||||
}
|
||||
sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
|
||||
if (sect.get()) {
|
||||
m_compact_unwind_up.reset(new CompactUnwindInfo(m_object_file, sect));
|
||||
}
|
||||
sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
|
||||
if (sect.get()) {
|
||||
SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
|
||||
if (sect_extab.get()) {
|
||||
m_arm_unwind_up.reset(
|
||||
new ArmUnwindInfo(m_object_file, sect, sect_extab));
|
||||
}
|
||||
}
|
||||
if (!sl)
|
||||
return;
|
||||
|
||||
SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
|
||||
if (sect.get()) {
|
||||
m_eh_frame_up.reset(new DWARFCallFrameInfo(m_object_file, sect,
|
||||
eRegisterKindEHFrame, true));
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
|
||||
if (sect) {
|
||||
m_debug_frame_up.reset(
|
||||
new DWARFCallFrameInfo(m_object_file, sect, eRegisterKindDWARF, false));
|
||||
}
|
||||
|
||||
sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
|
||||
if (sect) {
|
||||
m_compact_unwind_up.reset(new CompactUnwindInfo(m_object_file, sect));
|
||||
}
|
||||
|
||||
sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
|
||||
if (sect) {
|
||||
SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
|
||||
if (sect_extab.get()) {
|
||||
m_arm_unwind_up.reset(new ArmUnwindInfo(m_object_file, sect, sect_extab));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnwindTable::~UnwindTable() {}
|
||||
|
||||
llvm::Optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr,
|
||||
SymbolContext &sc) {
|
||||
AddressRange range;
|
||||
|
||||
// First check the symbol context
|
||||
if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
|
||||
false, range) &&
|
||||
range.GetBaseAddress().IsValid())
|
||||
return range;
|
||||
|
||||
// Does the eh_frame unwind info has a function bounds for this addr?
|
||||
if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
|
||||
return range;
|
||||
|
||||
// Try debug_frame as well
|
||||
if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
|
||||
return range;
|
||||
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
FuncUnwindersSP
|
||||
UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
|
||||
SymbolContext &sc) {
|
||||
FuncUnwindersSP no_unwind_found;
|
||||
|
||||
Initialize();
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_mutex);
|
||||
|
@ -96,23 +122,14 @@ UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
|
|||
return pos->second;
|
||||
}
|
||||
|
||||
AddressRange range;
|
||||
if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
|
||||
false, range) ||
|
||||
!range.GetBaseAddress().IsValid()) {
|
||||
// Does the eh_frame unwind info has a function bounds for this addr?
|
||||
if (m_eh_frame_up == nullptr ||
|
||||
!m_eh_frame_up->GetAddressRange(addr, range)) {
|
||||
return no_unwind_found;
|
||||
}
|
||||
}
|
||||
auto range_or = GetAddressRange(addr, sc);
|
||||
if (!range_or)
|
||||
return nullptr;
|
||||
|
||||
FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, range));
|
||||
FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or));
|
||||
m_unwinds.insert(insert_pos,
|
||||
std::make_pair(range.GetBaseAddress().GetFileAddress(),
|
||||
std::make_pair(range_or->GetBaseAddress().GetFileAddress(),
|
||||
func_unwinder_sp));
|
||||
// StreamFile s(stdout, false);
|
||||
// Dump (s);
|
||||
return func_unwinder_sp;
|
||||
}
|
||||
|
||||
|
@ -121,26 +138,16 @@ UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
|
|||
// UnwindTable. This is intended for use by target modules show-unwind where we
|
||||
// want to create
|
||||
// new UnwindPlans, not re-use existing ones.
|
||||
|
||||
FuncUnwindersSP
|
||||
UnwindTable::GetUncachedFuncUnwindersContainingAddress(const Address &addr,
|
||||
SymbolContext &sc) {
|
||||
FuncUnwindersSP no_unwind_found;
|
||||
Initialize();
|
||||
|
||||
AddressRange range;
|
||||
if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
|
||||
false, range) ||
|
||||
!range.GetBaseAddress().IsValid()) {
|
||||
// Does the eh_frame unwind info has a function bounds for this addr?
|
||||
if (m_eh_frame_up == nullptr ||
|
||||
!m_eh_frame_up->GetAddressRange(addr, range)) {
|
||||
return no_unwind_found;
|
||||
}
|
||||
}
|
||||
auto range_or = GetAddressRange(addr, sc);
|
||||
if (!range_or)
|
||||
return nullptr;
|
||||
|
||||
FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, range));
|
||||
return func_unwinder_sp;
|
||||
return std::make_shared<FuncUnwinders>(*this, *range_or);
|
||||
}
|
||||
|
||||
void UnwindTable::Dump(Stream &s) {
|
||||
|
@ -161,6 +168,11 @@ DWARFCallFrameInfo *UnwindTable::GetEHFrameInfo() {
|
|||
return m_eh_frame_up.get();
|
||||
}
|
||||
|
||||
DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
|
||||
Initialize();
|
||||
return m_debug_frame_up.get();
|
||||
}
|
||||
|
||||
CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
|
||||
Initialize();
|
||||
return m_compact_unwind_up.get();
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
add_lldb_unittest(SymbolTests
|
||||
TestClangASTContext.cpp
|
||||
TestDWARFCallFrameInfo.cpp
|
||||
TestType.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbHost
|
||||
lldbSymbol
|
||||
)
|
||||
|
||||
add_dependencies(SymbolTests yaml2obj)
|
||||
add_definitions(-DYAML2OBJ="$<TARGET_FILE:yaml2obj>")
|
||||
set(test_inputs
|
||||
basic-call-frame-info.yaml
|
||||
)
|
||||
add_unittest_inputs(SymbolTests "${test_inputs}")
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_DYN
|
||||
Machine: EM_X86_64
|
||||
Entry: 0x0000000000000260
|
||||
Sections:
|
||||
- Name: .text
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0x0000000000000260
|
||||
AddressAlign: 0x0000000000000010
|
||||
Content: 554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC3
|
||||
#0000000000000260 <eh_frame>:
|
||||
# 260: 55 push %rbp
|
||||
# 261: 48 89 e5 mov %rsp,%rbp
|
||||
# 264: 89 7d fc mov %edi,-0x4(%rbp)
|
||||
# 267: 8b 45 fc mov -0x4(%rbp),%eax
|
||||
# 26a: 5d pop %rbp
|
||||
# 26b: c3 retq
|
||||
# 26c: 0f 1f 40 00 nopl 0x0(%rax)
|
||||
#
|
||||
#0000000000000270 <debug_frame3>:
|
||||
# 270: 55 push %rbp
|
||||
# 271: 48 89 e5 mov %rsp,%rbp
|
||||
# 274: 89 7d fc mov %edi,-0x4(%rbp)
|
||||
# 277: 8b 45 fc mov -0x4(%rbp),%eax
|
||||
# 27a: 5d pop %rbp
|
||||
# 27b: c3 retq
|
||||
# 27c: 0f 1f 40 00 nopl 0x0(%rax)
|
||||
#
|
||||
#0000000000000280 <debug_frame4>:
|
||||
# 280: 55 push %rbp
|
||||
# 281: 48 89 e5 mov %rsp,%rbp
|
||||
# 284: 89 7d fc mov %edi,-0x4(%rbp)
|
||||
# 287: 8b 45 fc mov -0x4(%rbp),%eax
|
||||
# 28a: 5d pop %rbp
|
||||
# 28b: c3 retq
|
||||
- Name: .eh_frame
|
||||
Type: SHT_X86_64_UNWIND
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Address: 0x0000000000000290
|
||||
AddressAlign: 0x0000000000000008
|
||||
Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000
|
||||
#00000000 0000000000000014 00000000 CIE
|
||||
# Version: 1
|
||||
# Augmentation: "zR"
|
||||
# Code alignment factor: 1
|
||||
# Data alignment factor: -8
|
||||
# Return address column: 16
|
||||
# Augmentation data: 1b
|
||||
#
|
||||
# DW_CFA_def_cfa: r7 (rsp) ofs 8
|
||||
# DW_CFA_offset: r16 (rip) at cfa-8
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
#
|
||||
#00000018 000000000000001c 0000001c FDE cie=00000000 pc=ffffffffffffffd0..ffffffffffffffdc
|
||||
# DW_CFA_advance_loc: 1 to ffffffffffffffd1
|
||||
# DW_CFA_def_cfa_offset: 16
|
||||
# DW_CFA_offset: r6 (rbp) at cfa-16
|
||||
# DW_CFA_advance_loc: 3 to ffffffffffffffd4
|
||||
# DW_CFA_def_cfa_register: r6 (rbp)
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
- Name: .debug_frame
|
||||
Type: SHT_PROGBITS
|
||||
AddressAlign: 0x0000000000000008
|
||||
Content: 14000000FFFFFFFF03000178100C070890010000000000001C0000000000000070020000000000000C00000000000000410E108602430D0614000000FFFFFFFF040008000178100C07089001000000001C0000003800000080020000000000000C00000000000000410E108602430D06
|
||||
#00000000 0000000000000014 ffffffff CIE
|
||||
# Version: 3
|
||||
# Augmentation: ""
|
||||
# Code alignment factor: 1
|
||||
# Data alignment factor: -8
|
||||
# Return address column: 16
|
||||
#
|
||||
# DW_CFA_def_cfa: r7 (rsp) ofs 8
|
||||
# DW_CFA_offset: r16 (rip) at cfa-8
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
#
|
||||
#00000018 000000000000001c 00000000 FDE cie=00000000 pc=0000000000000270..000000000000027c
|
||||
# DW_CFA_advance_loc: 1 to 0000000000000271
|
||||
# DW_CFA_def_cfa_offset: 16
|
||||
# DW_CFA_offset: r6 (rbp) at cfa-16
|
||||
# DW_CFA_advance_loc: 3 to 0000000000000274
|
||||
# DW_CFA_def_cfa_register: r6 (rbp)
|
||||
#
|
||||
#00000038 0000000000000014 ffffffff CIE
|
||||
# Version: 4
|
||||
# Augmentation: ""
|
||||
# Pointer Size: 8
|
||||
# Segment Size: 0
|
||||
# Code alignment factor: 1
|
||||
# Data alignment factor: -8
|
||||
# Return address column: 16
|
||||
#
|
||||
# DW_CFA_def_cfa: r7 (rsp) ofs 8
|
||||
# DW_CFA_offset: r16 (rip) at cfa-8
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
# DW_CFA_nop
|
||||
#
|
||||
#00000050 000000000000001c 00000038 FDE cie=00000038 pc=0000000000000280..000000000000028c
|
||||
# DW_CFA_advance_loc: 1 to 0000000000000281
|
||||
# DW_CFA_def_cfa_offset: 16
|
||||
# DW_CFA_offset: r6 (rbp) at cfa-16
|
||||
# DW_CFA_advance_loc: 3 to 0000000000000284
|
||||
# DW_CFA_def_cfa_register: r6 (rbp)
|
||||
Symbols:
|
||||
Global:
|
||||
- Name: eh_frame
|
||||
Type: STT_FUNC
|
||||
Section: .text
|
||||
Value: 0x0000000000000260
|
||||
Size: 0x000000000000000C
|
||||
- Name: debug_frame3
|
||||
Type: STT_FUNC
|
||||
Section: .text
|
||||
Value: 0x0000000000000270
|
||||
Size: 0x000000000000000C
|
||||
- Name: debug_frame4
|
||||
Type: STT_FUNC
|
||||
Section: .text
|
||||
Value: 0x0000000000000280
|
||||
Size: 0x000000000000000C
|
||||
...
|
|
@ -0,0 +1,147 @@
|
|||
//===-- TestDWARFCallFrameInfo.cpp ------------------------------*- C++ -*-===//
|
||||
//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
|
||||
#include "Plugins/Process/Utility/RegisterContext_x86.h"
|
||||
#include "lldb/Core/Module.h"
|
||||
#include "lldb/Core/ModuleSpec.h"
|
||||
#include "lldb/Core/Section.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
#include "lldb/Symbol/DWARFCallFrameInfo.h"
|
||||
#include "lldb/Utility/StreamString.h"
|
||||
#include "llvm/Support/FileUtilities.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
extern const char *TestMainArgv0;
|
||||
|
||||
using namespace lldb_private;
|
||||
using namespace lldb;
|
||||
|
||||
class DWARFCallFrameInfoTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
HostInfo::Initialize();
|
||||
ObjectFileELF::Initialize();
|
||||
|
||||
m_inputs_folder = llvm::sys::path::parent_path(TestMainArgv0);
|
||||
llvm::sys::path::append(m_inputs_folder, "Inputs");
|
||||
llvm::sys::fs::make_absolute(m_inputs_folder);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
ObjectFileELF::Terminate();
|
||||
HostInfo::Terminate();
|
||||
}
|
||||
|
||||
protected:
|
||||
llvm::SmallString<128> m_inputs_folder;
|
||||
|
||||
void TestBasic(bool eh_frame, llvm::StringRef symbol);
|
||||
};
|
||||
|
||||
#define ASSERT_NO_ERROR(x) \
|
||||
if (std::error_code ASSERT_NO_ERROR_ec = x) { \
|
||||
llvm::SmallString<128> MessageStorage; \
|
||||
llvm::raw_svector_ostream Message(MessageStorage); \
|
||||
Message << #x ": did not return errc::success.\n" \
|
||||
<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
|
||||
<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
|
||||
GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
|
||||
} else { \
|
||||
}
|
||||
|
||||
namespace lldb_private {
|
||||
static std::ostream &operator<<(std::ostream &OS, const UnwindPlan::Row &row) {
|
||||
StreamString SS;
|
||||
row.Dump(SS, nullptr, nullptr, 0);
|
||||
return OS << SS.GetData();
|
||||
}
|
||||
} // namespace lldb_private
|
||||
|
||||
static UnwindPlan::Row GetExpectedRow0() {
|
||||
UnwindPlan::Row row;
|
||||
row.SetOffset(0);
|
||||
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 8);
|
||||
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
|
||||
return row;
|
||||
}
|
||||
|
||||
static UnwindPlan::Row GetExpectedRow1() {
|
||||
UnwindPlan::Row row;
|
||||
row.SetOffset(1);
|
||||
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 16);
|
||||
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
|
||||
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
|
||||
return row;
|
||||
}
|
||||
|
||||
static UnwindPlan::Row GetExpectedRow2() {
|
||||
UnwindPlan::Row row;
|
||||
row.SetOffset(4);
|
||||
row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp_x86_64, 16);
|
||||
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
|
||||
row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
|
||||
return row;
|
||||
}
|
||||
|
||||
void DWARFCallFrameInfoTest::TestBasic(bool eh_frame, llvm::StringRef symbol) {
|
||||
llvm::SmallString<128> yaml = m_inputs_folder;
|
||||
llvm::sys::path::append(yaml, "basic-call-frame-info.yaml");
|
||||
llvm::SmallString<128> obj = m_inputs_folder;
|
||||
|
||||
ASSERT_NO_ERROR(llvm::sys::fs::createTemporaryFile(
|
||||
"basic-call-frame-info-%%%%%%", "obj", obj));
|
||||
llvm::FileRemover obj_remover(obj);
|
||||
|
||||
const char *args[] = {YAML2OBJ, yaml.c_str(), nullptr};
|
||||
llvm::StringRef obj_ref = obj;
|
||||
const llvm::StringRef *redirects[] = {nullptr, &obj_ref, nullptr};
|
||||
ASSERT_EQ(0, llvm::sys::ExecuteAndWait(YAML2OBJ, args, nullptr, redirects));
|
||||
|
||||
uint64_t size;
|
||||
ASSERT_NO_ERROR(llvm::sys::fs::file_size(obj, size));
|
||||
ASSERT_GT(size, 0u);
|
||||
|
||||
auto module_sp = std::make_shared<Module>(ModuleSpec(FileSpec(obj, false)));
|
||||
SectionList *list = module_sp->GetSectionList();
|
||||
ASSERT_NE(nullptr, list);
|
||||
|
||||
auto section_sp = list->FindSectionByType(
|
||||
eh_frame ? eSectionTypeEHFrame : eSectionTypeDWARFDebugFrame, false);
|
||||
ASSERT_NE(nullptr, section_sp);
|
||||
|
||||
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
|
||||
eh_frame ? eRegisterKindEHFrame : eRegisterKindDWARF,
|
||||
eh_frame);
|
||||
|
||||
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
|
||||
ConstString(symbol), eSymbolTypeAny);
|
||||
ASSERT_NE(nullptr, sym);
|
||||
|
||||
UnwindPlan plan(eRegisterKindGeneric);
|
||||
ASSERT_TRUE(cfi.GetUnwindPlan(sym->GetAddress(), plan));
|
||||
ASSERT_EQ(3, plan.GetRowCount());
|
||||
EXPECT_EQ(GetExpectedRow0(), *plan.GetRowAtIndex(0));
|
||||
EXPECT_EQ(GetExpectedRow1(), *plan.GetRowAtIndex(1));
|
||||
EXPECT_EQ(GetExpectedRow2(), *plan.GetRowAtIndex(2));
|
||||
}
|
||||
|
||||
TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) {
|
||||
TestBasic(false, "debug_frame3");
|
||||
}
|
||||
|
||||
TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) {
|
||||
TestBasic(false, "debug_frame4");
|
||||
}
|
||||
|
||||
TEST_F(DWARFCallFrameInfoTest, Basic_eh) { TestBasic(true, "eh_frame"); }
|
Loading…
Reference in New Issue