diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index 2f8f9e9182fb2..b490045cb3818 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -20,6 +20,8 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Timer.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include #include #include #include @@ -147,6 +149,23 @@ GetGNUEHPointer(const DataExtractor &DE, lldb::offset_t *offset_ptr, return baseAddress + addressValue; } +// Check if the given cie_id value indicates a CIE (Common Information Entry) +// as opposed to an FDE (Frame Description Entry). +static bool IsCIEMarker(uint64_t cie_id, bool is_64bit, + DWARFCallFrameInfo::Type type) { + // Check eh_frame CIE marker + if (type == DWARFCallFrameInfo::EH) + return cie_id == 0; + + // Check debug_frame CIE marker + // DWARF64 + if (is_64bit) + return cie_id == llvm::dwarf::DW64_CIE_ID; + + // DWARF32 + return cie_id == llvm::dwarf::DW_CIE_ID; +} + DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile, SectionSP §ion_sp, Type type) : m_objfile(objfile), m_section_sp(section_sp), m_type(type) {} @@ -283,7 +302,7 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) { GetCFIData(); uint32_t length = m_cfi_data.GetU32(&offset); dw_offset_t cie_id, end_offset; - bool is_64bit = (length == UINT32_MAX); + bool is_64bit = (length == llvm::dwarf::DW_LENGTH_DWARF64); if (is_64bit) { length = m_cfi_data.GetU64(&offset); cie_id = m_cfi_data.GetU64(&offset); @@ -292,8 +311,9 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) { cie_id = m_cfi_data.GetU32(&offset); end_offset = cie_offset + length + 4; } - if (length > 0 && ((m_type == DWARF && cie_id == UINT32_MAX) || - (m_type == EH && cie_id == 0ul))) { + + // Check if this is a CIE or FDE based on the CIE ID marker + if (length > 0 && IsCIEMarker(cie_id, is_64bit, m_type)) { size_t i; // cie.offset = cie_offset; // cie.length = length; @@ -470,7 +490,7 @@ void DWARFCallFrameInfo::GetFDEIndex() { const dw_offset_t current_entry = offset; dw_offset_t cie_id, next_entry, cie_offset; uint32_t len = m_cfi_data.GetU32(&offset); - bool is_64bit = (len == UINT32_MAX); + bool is_64bit = (len == llvm::dwarf::DW_LENGTH_DWARF64); if (is_64bit) { len = m_cfi_data.GetU64(&offset); cie_id = m_cfi_data.GetU64(&offset); @@ -493,11 +513,8 @@ void DWARFCallFrameInfo::GetFDEIndex() { 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_type == EH) || cie_id == UINT32_MAX || len == 0) { + // Check if this is a CIE or FDE based on the CIE ID marker + if (IsCIEMarker(cie_id, is_64bit, m_type) || len == 0) { auto cie_sp = ParseCIE(current_entry); if (!cie_sp) { // Cannot parse, the reason is already logged @@ -568,7 +585,7 @@ DWARFCallFrameInfo::ParseFDE(dw_offset_t dwarf_offset, uint32_t length = m_cfi_data.GetU32(&offset); dw_offset_t cie_offset; - bool is_64bit = (length == UINT32_MAX); + bool is_64bit = (length == llvm::dwarf::DW_LENGTH_DWARF64); if (is_64bit) { length = m_cfi_data.GetU64(&offset); cie_offset = m_cfi_data.GetU64(&offset); @@ -577,7 +594,9 @@ DWARFCallFrameInfo::ParseFDE(dw_offset_t dwarf_offset, } // FDE entries with zeroth cie_offset may occur for debug_frame. - assert(!(m_type == EH && 0 == cie_offset) && cie_offset != UINT32_MAX); + assert(!(m_type == EH && 0 == cie_offset) && + cie_offset != + (is_64bit ? llvm::dwarf::DW64_CIE_ID : llvm::dwarf::DW_CIE_ID)); // Translate the CIE_id from the eh_frame format, which is relative to the // FDE offset, into a __eh_frame section offset diff --git a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp index e113b8ca99341..c52e9a7387e14 100644 --- a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp +++ b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -380,3 +380,288 @@ void DWARFCallFrameInfoTest::TestValOffset(DWARFCallFrameInfo::Type type, TEST_F(DWARFCallFrameInfoTest, ValOffset_dwarf3) { TestValOffset(DWARFCallFrameInfo::DWARF, "debug_frame3"); } + +// Test that we correctly handle invalid FDE entries that have CIE ID values +TEST_F(DWARFCallFrameInfoTest, InvalidFDEWithCIEID_dwarf32) { + // Create an FDE with cie_offset of 0xFFFFFFFF (DW_CIE_ID) which is invalid + auto ExpectedFile = TestFile::fromYaml(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000000260 + AddressAlign: 0x0000000000000010 + Content: 554889E5897DFC8B45FC5DC3 + - Name: .debug_frame + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000008 + # First, a valid CIE + # 00000000 0000000000000014 ffffffff CIE + # Version: 3 + # Augmentation: "" + # Code alignment factor: 1 + # Data alignment factor: -8 + # Return address column: 16 + Content: 14000000FFFFFFFF03000178100C0708900100000000000018000000FFFFFFFF60020000000000000C00000000000000 + # Then an invalid FDE with CIE pointer = 0xFFFFFFFF (which would make it look like a CIE) + # 00000018 0000000000000018 ffffffff FDE cie=ffffffff pc=0000000000000260..000000000000026c + # The cie offset of 0xFFFFFFFF is invalid for an FDE in debug_frame +Symbols: + - Name: test_invalid + Type: STT_FUNC + Section: .text + Value: 0x0000000000000260 + Size: 0x000000000000000C + Binding: STB_GLOBAL +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, + DWARFCallFrameInfo::DWARF); + + // This should trigger our assertion or return nullptr because the FDE is + // invalid + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString("test_invalid"), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + // The plan should be null because we have an invalid FDE + EXPECT_EQ(nullptr, plan_up); +} + +// Test that we correctly handle invalid FDE entries that have CIE ID values +TEST_F(DWARFCallFrameInfoTest, InvalidFDEWithCIEID_dwarf64) { + // Create an FDE with cie_offset of 0xFFFFFFFFFFFFFFFF (DW64_CIE_ID) which is + // invalid + auto ExpectedFile = TestFile::fromYaml(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000000260 + AddressAlign: 0x0000000000000010 + Content: 554889E5897DFC8B45FC5DC3 + - Name: .debug_frame + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000008 + # DWARF64 format CIE + # Initial length: 0xFFFFFFFF followed by 64-bit length + # 00000000 ffffffff 0000000000000014 ffffffffffffffff CIE + Content: FFFFFFFF1400000000000000FFFFFFFFFFFFFFFF03000178100C0708900100000000FFFFFFFF1800000000000000FFFFFFFFFFFFFFFF60020000000000000C00000000000000 + # DWARF64 FDE with invalid CIE pointer = 0xFFFFFFFFFFFFFFFF + # Initial length: 0xFFFFFFFF, followed by 64-bit length (0x18) + # Then 64-bit CIE pointer: 0xFFFFFFFFFFFFFFFF (which is DW64_CIE_ID, invalid for FDE) +Symbols: + - Name: test_invalid64 + Type: STT_FUNC + Section: .text + Value: 0x0000000000000260 + Size: 0x000000000000000C + Binding: STB_GLOBAL +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, + DWARFCallFrameInfo::DWARF); + + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString("test_invalid64"), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + // The plan should be null because we have an invalid FDE + EXPECT_EQ(nullptr, plan_up); +} + +// Test valid CIE markers in eh_frame format +TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_eh_frame) { + auto ExpectedFile = TestFile::fromYaml(R"( +--- !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: 554889E5897DFC8B45FC5DC3 + - Name: .eh_frame + Type: SHT_X86_64_UNWIND + Flags: [ SHF_ALLOC ] + Address: 0x0000000000000290 + AddressAlign: 0x0000000000000008 + # eh_frame content + # CIE + FDE that works with address 0x260 + Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000 +Symbols: + - Name: simple_function + Type: STT_FUNC + Section: .text + Value: 0x0000000000000260 + Size: 0x000000000000000F + Binding: STB_GLOBAL +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(eSectionTypeEHFrame, false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, + DWARFCallFrameInfo::EH); + + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString("simple_function"), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + // Should succeed with valid CIE and FDE + ASSERT_NE(nullptr, plan_up); + EXPECT_GE(plan_up->GetRowCount(), 1); +} + +// Test valid CIE markers in debug_frame DWARF32 format +TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_dwarf32) { + auto ExpectedFile = TestFile::fromYaml(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000001130 + AddressAlign: 0x0000000000000010 + Content: 554889E5897DFC8B45FC83C0015DC3 + - Name: .debug_frame + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000008 + # debug_frame content in DWARF32 format + # CIE (length=0x14, CIE_id=0xFFFFFFFF, version=4) + # FDE (length=0x24, CIE_offset=0) + Content: 14000000FFFFFFFF040008000178100C0708900100000000240000000000000030110000000000000F00000000000000410E108602430D064A0C070800000000 +Symbols: + - Name: simple_function + Type: STT_FUNC + Section: .text + Value: 0x0000000000001130 + Size: 0x000000000000000F + Binding: STB_GLOBAL +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, + DWARFCallFrameInfo::DWARF); + + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString("simple_function"), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + // Should succeed with valid CIE and FDE + ASSERT_NE(nullptr, plan_up); + EXPECT_GE(plan_up->GetRowCount(), 1); +} + +// Test valid CIE markers in debug_frame DWARF64 format +TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_dwarf64) { + auto ExpectedFile = TestFile::fromYaml(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000000000001130 + AddressAlign: 0x0000000000000010 + Content: 554889E5897DFC8B45FC83C0015DC3 + - Name: .debug_frame + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000008 + # debug_frame content in DWARF64 format + # CIE: length_marker=0xFFFFFFFF, length=0x14, CIE_id=0xFFFFFFFFFFFFFFFF, version=4 + # FDE: length_marker=0xFFFFFFFF, length=0x24, CIE_offset=0x0 (points to CIE) + Content: FFFFFFFF1400000000000000FFFFFFFFFFFFFFFF040008000178100C07089001FFFFFFFF2400000000000000000000000000000030110000000000000F00000000000000410E108602430D064A0C0708 +Symbols: + - Name: simple_function + Type: STT_FUNC + Section: .text + Value: 0x0000000000001130 + Size: 0x000000000000000F + Binding: STB_GLOBAL +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, + DWARFCallFrameInfo::DWARF); + + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString("simple_function"), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + // Should succeed with valid CIE and FDE + ASSERT_NE(nullptr, plan_up); + EXPECT_GE(plan_up->GetRowCount(), 1); +}