Index: include/lldb/Symbol/DWARFCallFrameInfo.h =================================================================== --- include/lldb/Symbol/DWARFCallFrameInfo.h +++ include/lldb/Symbol/DWARFCallFrameInfo.h @@ -37,7 +37,7 @@ 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 @@ 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; Index: include/lldb/Symbol/FuncUnwinders.h =================================================================== --- include/lldb/Symbol/FuncUnwinders.h +++ include/lldb/Symbol/FuncUnwinders.h @@ -99,6 +99,12 @@ 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 +132,12 @@ 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 m_unwind_plan_compact_unwind; lldb::UnwindPlanSP m_unwind_plan_arm_unwind_sp; lldb::UnwindPlanSP m_unwind_plan_fast_sp; @@ -139,7 +147,9 @@ // 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, Index: include/lldb/Symbol/UnwindTable.h =================================================================== --- include/lldb/Symbol/UnwindTable.h +++ include/lldb/Symbol/UnwindTable.h @@ -27,6 +27,7 @@ ~UnwindTable(); lldb_private::DWARFCallFrameInfo *GetEHFrameInfo(); + lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo(); lldb_private::CompactUnwindInfo *GetCompactUnwindInfo(); @@ -58,6 +59,7 @@ void Dump(Stream &s); void Initialize(); + llvm::Optional GetAddressRange(const Address &addr, SymbolContext &sc); typedef std::map collection; typedef collection::iterator iterator; @@ -70,6 +72,7 @@ std::mutex m_mutex; std::unique_ptr m_eh_frame_up; + std::unique_ptr m_debug_frame_up; std::unique_ptr m_compact_unwind_up; std::unique_ptr m_arm_unwind_up; Index: source/Commands/CommandObjectTarget.cpp =================================================================== --- source/Commands/CommandObjectTarget.cpp +++ source/Commands/CommandObjectTarget.cpp @@ -3426,6 +3426,23 @@ 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) { Index: source/Symbol/DWARFCallFrameInfo.cpp =================================================================== --- source/Symbol/DWARFCallFrameInfo.cpp +++ source/Symbol/DWARFCallFrameInfo.cpp @@ -161,8 +161,6 @@ 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 @@ // 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 @@ "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(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 @@ 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 @@ 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 @@ 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 Index: source/Symbol/FuncUnwinders.cpp =================================================================== --- source/Symbol/FuncUnwinders.cpp +++ source/Symbol/FuncUnwinders.cpp @@ -39,7 +39,9 @@ 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 @@ int current_offset) { std::lock_guard 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,28 @@ return m_unwind_plan_eh_frame_sp; } +UnwindPlanSP FuncUnwinders::GetDebugFrameUnwindPlan(Target &target, + int current_offset) { + std::lock_guard 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 guard(m_mutex); @@ -187,6 +208,46 @@ return m_unwind_plan_eh_frame_augmented_sp; } +UnwindPlanSP +FuncUnwinders::GetDebugFrameAugmentedUnwindPlan(Target &target, Thread &thread, + int current_offset) { + std::lock_guard 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 +309,8 @@ 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 +318,22 @@ 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 +344,12 @@ 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; } Index: source/Symbol/UnwindTable.cpp =================================================================== --- source/Symbol/UnwindTable.cpp +++ source/Symbol/UnwindTable.cpp @@ -44,38 +44,63 @@ 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 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 guard(m_mutex); @@ -96,23 +121,14 @@ 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 +137,16 @@ // 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(*this, *range_or); } void UnwindTable::Dump(Stream &s) { @@ -161,6 +167,11 @@ 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(); Index: unittests/Symbol/CMakeLists.txt =================================================================== --- unittests/Symbol/CMakeLists.txt +++ unittests/Symbol/CMakeLists.txt @@ -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="$") +set(test_inputs + basic-call-frame-info.yaml + ) +add_unittest_inputs(SymbolTests "${test_inputs}") Index: unittests/Symbol/Inputs/basic-call-frame-info.yaml =================================================================== --- /dev/null +++ unittests/Symbol/Inputs/basic-call-frame-info.yaml @@ -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 : +# 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 : +# 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 : +# 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 +... Index: unittests/Symbol/TestDWARFCallFrameInfo.cpp =================================================================== --- /dev/null +++ unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -0,0 +1,149 @@ +//===-- 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(); +} +} + +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(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"); +}