Index: include/lldb/Symbol/DWARFCallFrameInfo.h =================================================================== --- include/lldb/Symbol/DWARFCallFrameInfo.h +++ include/lldb/Symbol/DWARFCallFrameInfo.h @@ -25,6 +25,11 @@ namespace lldb_private { +enum class DwarfVersion : uint16_t { + eMinSupported = 2, // DWARF version 1 is only a draft, it is not supported + eMaxSupported = 5 +}; + // DWARFCallFrameInfo is a class which can read eh_frame and DWARF // Call Frame Information FDEs. It stores little information internally. // Only two APIs are exported - one to find the high/low pc values @@ -37,7 +42,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,6 +79,11 @@ 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; @@ -80,6 +90,9 @@ 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: 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,12 +298,24 @@ "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 // which starts with a ULEB128 length in bytes @@ -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 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/UnwindTable.cpp =================================================================== --- source/Symbol/UnwindTable.cpp +++ source/Symbol/UnwindTable.cpp @@ -51,6 +51,30 @@ if (sect.get()) { m_eh_frame_up.reset(new DWARFCallFrameInfo(m_object_file, sect, eRegisterKindEHFrame, true)); + } else { + // Try to find .debug_frame section even if .eh_frame doesn't exist. + // If there is not debug_info and DWARF version is unknown, + // create DWARFCallFrameInfo anyway + uint16_t version = static_cast(DwarfVersion::eMinSupported); + sect = sl->FindSectionByType(eSectionTypeDWARFDebugInfo, true); + if (sect) { + uint32_t skipped_field = 0; + lldb::offset_t offset = 0; + sect->GetSectionData(&skipped_field, sizeof(skipped_field), offset); + + offset = UINT32_MAX == skipped_field ? sizeof(skipped_field) * 3 + : sizeof(skipped_field); + + sect->GetSectionData(&version, sizeof(version), offset); + } + if (version >= static_cast(DwarfVersion::eMinSupported) && + version <= static_cast(DwarfVersion::eMaxSupported)) { + sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true); + if (sect) { + m_eh_frame_up.reset(new DWARFCallFrameInfo( + m_object_file, sect, eRegisterKindDWARF, false)); + } + } } sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true); if (sect.get()) {