[LLDB] Fix 64 bit support for CIE and FDE handling in DWARFCallFrameInfo (#158350)
- Introduced a new helper function `IsCIEMarker` to determine if a given
`cie_id` indicates a CIE (Common Information Entry) or FDE (Frame
Description Entry).
- New helper function can now correctly identify both 32-bit and 64-bit
CIE based on the DWARF specifications.
- Updated the `ParseCIE` and `GetFDEIndex` methods to utilize the new
helper function for improved clarity and correctness in identifying CIE
and FDE entries.
- Replaced direct comparisons with `UINT32_MAX` and `UINT64_MAX` with
`std::numeric_limits` for better readability.
diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp
index 2f8f9e9..b490045 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 <cstdint>
#include <cstring>
#include <list>
#include <optional>
@@ -147,6 +149,23 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 e113b8c..c52e9a7 100644
--- a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp
+++ b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp
@@ -380,3 +380,288 @@
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<Module>(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<UnwindPlan> 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<Module>(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<UnwindPlan> 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<Module>(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<UnwindPlan> 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<Module>(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<UnwindPlan> 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<Module>(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<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
+ // Should succeed with valid CIE and FDE
+ ASSERT_NE(nullptr, plan_up);
+ EXPECT_GE(plan_up->GetRowCount(), 1);
+}