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:
Pavel Labath 2017-06-27 11:16:26 +00:00
parent 7caff0e9e1
commit cdda23eb7c
10 changed files with 544 additions and 105 deletions

View File

@ -37,7 +37,7 @@ public:
DWARFCallFrameInfo(ObjectFile &objfile, lldb::SectionSP &section,
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;

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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}")

View File

@ -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
...

View File

@ -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"); }