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,6 +74,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 +85,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 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/UnwindTable.cpp =================================================================== --- source/Symbol/UnwindTable.cpp +++ source/Symbol/UnwindTable.cpp @@ -51,6 +51,13 @@ 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. + 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()) {