Skip to content

Commit c31d503

Browse files
authored
[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.
1 parent f693a7f commit c31d503

File tree

2 files changed

+315
-11
lines changed

2 files changed

+315
-11
lines changed

lldb/source/Symbol/DWARFCallFrameInfo.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "lldb/Utility/LLDBLog.h"
2121
#include "lldb/Utility/Log.h"
2222
#include "lldb/Utility/Timer.h"
23+
#include "llvm/BinaryFormat/Dwarf.h"
24+
#include <cstdint>
2325
#include <cstring>
2426
#include <list>
2527
#include <optional>
@@ -147,6 +149,23 @@ GetGNUEHPointer(const DataExtractor &DE, lldb::offset_t *offset_ptr,
147149
return baseAddress + addressValue;
148150
}
149151

152+
// Check if the given cie_id value indicates a CIE (Common Information Entry)
153+
// as opposed to an FDE (Frame Description Entry).
154+
static bool IsCIEMarker(uint64_t cie_id, bool is_64bit,
155+
DWARFCallFrameInfo::Type type) {
156+
// Check eh_frame CIE marker
157+
if (type == DWARFCallFrameInfo::EH)
158+
return cie_id == 0;
159+
160+
// Check debug_frame CIE marker
161+
// DWARF64
162+
if (is_64bit)
163+
return cie_id == llvm::dwarf::DW64_CIE_ID;
164+
165+
// DWARF32
166+
return cie_id == llvm::dwarf::DW_CIE_ID;
167+
}
168+
150169
DWARFCallFrameInfo::DWARFCallFrameInfo(ObjectFile &objfile,
151170
SectionSP &section_sp, Type type)
152171
: m_objfile(objfile), m_section_sp(section_sp), m_type(type) {}
@@ -283,7 +302,7 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
283302
GetCFIData();
284303
uint32_t length = m_cfi_data.GetU32(&offset);
285304
dw_offset_t cie_id, end_offset;
286-
bool is_64bit = (length == UINT32_MAX);
305+
bool is_64bit = (length == llvm::dwarf::DW_LENGTH_DWARF64);
287306
if (is_64bit) {
288307
length = m_cfi_data.GetU64(&offset);
289308
cie_id = m_cfi_data.GetU64(&offset);
@@ -292,8 +311,9 @@ DWARFCallFrameInfo::ParseCIE(const dw_offset_t cie_offset) {
292311
cie_id = m_cfi_data.GetU32(&offset);
293312
end_offset = cie_offset + length + 4;
294313
}
295-
if (length > 0 && ((m_type == DWARF && cie_id == UINT32_MAX) ||
296-
(m_type == EH && cie_id == 0ul))) {
314+
315+
// Check if this is a CIE or FDE based on the CIE ID marker
316+
if (length > 0 && IsCIEMarker(cie_id, is_64bit, m_type)) {
297317
size_t i;
298318
// cie.offset = cie_offset;
299319
// cie.length = length;
@@ -470,7 +490,7 @@ void DWARFCallFrameInfo::GetFDEIndex() {
470490
const dw_offset_t current_entry = offset;
471491
dw_offset_t cie_id, next_entry, cie_offset;
472492
uint32_t len = m_cfi_data.GetU32(&offset);
473-
bool is_64bit = (len == UINT32_MAX);
493+
bool is_64bit = (len == llvm::dwarf::DW_LENGTH_DWARF64);
474494
if (is_64bit) {
475495
len = m_cfi_data.GetU64(&offset);
476496
cie_id = m_cfi_data.GetU64(&offset);
@@ -493,11 +513,8 @@ void DWARFCallFrameInfo::GetFDEIndex() {
493513
return;
494514
}
495515

496-
// An FDE entry contains CIE_pointer in debug_frame in same place as cie_id
497-
// in eh_frame. CIE_pointer is an offset into the .debug_frame section. So,
498-
// variable cie_offset should be equal to cie_id for debug_frame.
499-
// FDE entries with cie_id == 0 shouldn't be ignored for it.
500-
if ((cie_id == 0 && m_type == EH) || cie_id == UINT32_MAX || len == 0) {
516+
// Check if this is a CIE or FDE based on the CIE ID marker
517+
if (IsCIEMarker(cie_id, is_64bit, m_type) || len == 0) {
501518
auto cie_sp = ParseCIE(current_entry);
502519
if (!cie_sp) {
503520
// Cannot parse, the reason is already logged
@@ -568,7 +585,7 @@ DWARFCallFrameInfo::ParseFDE(dw_offset_t dwarf_offset,
568585

569586
uint32_t length = m_cfi_data.GetU32(&offset);
570587
dw_offset_t cie_offset;
571-
bool is_64bit = (length == UINT32_MAX);
588+
bool is_64bit = (length == llvm::dwarf::DW_LENGTH_DWARF64);
572589
if (is_64bit) {
573590
length = m_cfi_data.GetU64(&offset);
574591
cie_offset = m_cfi_data.GetU64(&offset);
@@ -577,7 +594,9 @@ DWARFCallFrameInfo::ParseFDE(dw_offset_t dwarf_offset,
577594
}
578595

579596
// FDE entries with zeroth cie_offset may occur for debug_frame.
580-
assert(!(m_type == EH && 0 == cie_offset) && cie_offset != UINT32_MAX);
597+
assert(!(m_type == EH && 0 == cie_offset) &&
598+
cie_offset !=
599+
(is_64bit ? llvm::dwarf::DW64_CIE_ID : llvm::dwarf::DW_CIE_ID));
581600

582601
// Translate the CIE_id from the eh_frame format, which is relative to the
583602
// FDE offset, into a __eh_frame section offset

lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,288 @@ void DWARFCallFrameInfoTest::TestValOffset(DWARFCallFrameInfo::Type type,
380380
TEST_F(DWARFCallFrameInfoTest, ValOffset_dwarf3) {
381381
TestValOffset(DWARFCallFrameInfo::DWARF, "debug_frame3");
382382
}
383+
384+
// Test that we correctly handle invalid FDE entries that have CIE ID values
385+
TEST_F(DWARFCallFrameInfoTest, InvalidFDEWithCIEID_dwarf32) {
386+
// Create an FDE with cie_offset of 0xFFFFFFFF (DW_CIE_ID) which is invalid
387+
auto ExpectedFile = TestFile::fromYaml(R"(
388+
--- !ELF
389+
FileHeader:
390+
Class: ELFCLASS64
391+
Data: ELFDATA2LSB
392+
Type: ET_REL
393+
Machine: EM_X86_64
394+
Sections:
395+
- Name: .text
396+
Type: SHT_PROGBITS
397+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
398+
Address: 0x0000000000000260
399+
AddressAlign: 0x0000000000000010
400+
Content: 554889E5897DFC8B45FC5DC3
401+
- Name: .debug_frame
402+
Type: SHT_PROGBITS
403+
AddressAlign: 0x0000000000000008
404+
# First, a valid CIE
405+
# 00000000 0000000000000014 ffffffff CIE
406+
# Version: 3
407+
# Augmentation: ""
408+
# Code alignment factor: 1
409+
# Data alignment factor: -8
410+
# Return address column: 16
411+
Content: 14000000FFFFFFFF03000178100C0708900100000000000018000000FFFFFFFF60020000000000000C00000000000000
412+
# Then an invalid FDE with CIE pointer = 0xFFFFFFFF (which would make it look like a CIE)
413+
# 00000018 0000000000000018 ffffffff FDE cie=ffffffff pc=0000000000000260..000000000000026c
414+
# The cie offset of 0xFFFFFFFF is invalid for an FDE in debug_frame
415+
Symbols:
416+
- Name: test_invalid
417+
Type: STT_FUNC
418+
Section: .text
419+
Value: 0x0000000000000260
420+
Size: 0x000000000000000C
421+
Binding: STB_GLOBAL
422+
...
423+
)");
424+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
425+
426+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
427+
SectionList *list = module_sp->GetSectionList();
428+
ASSERT_NE(nullptr, list);
429+
430+
auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false);
431+
ASSERT_NE(nullptr, section_sp);
432+
433+
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
434+
DWARFCallFrameInfo::DWARF);
435+
436+
// This should trigger our assertion or return nullptr because the FDE is
437+
// invalid
438+
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
439+
ConstString("test_invalid"), eSymbolTypeAny);
440+
ASSERT_NE(nullptr, sym);
441+
442+
std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
443+
// The plan should be null because we have an invalid FDE
444+
EXPECT_EQ(nullptr, plan_up);
445+
}
446+
447+
// Test that we correctly handle invalid FDE entries that have CIE ID values
448+
TEST_F(DWARFCallFrameInfoTest, InvalidFDEWithCIEID_dwarf64) {
449+
// Create an FDE with cie_offset of 0xFFFFFFFFFFFFFFFF (DW64_CIE_ID) which is
450+
// invalid
451+
auto ExpectedFile = TestFile::fromYaml(R"(
452+
--- !ELF
453+
FileHeader:
454+
Class: ELFCLASS64
455+
Data: ELFDATA2LSB
456+
Type: ET_REL
457+
Machine: EM_X86_64
458+
Sections:
459+
- Name: .text
460+
Type: SHT_PROGBITS
461+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
462+
Address: 0x0000000000000260
463+
AddressAlign: 0x0000000000000010
464+
Content: 554889E5897DFC8B45FC5DC3
465+
- Name: .debug_frame
466+
Type: SHT_PROGBITS
467+
AddressAlign: 0x0000000000000008
468+
# DWARF64 format CIE
469+
# Initial length: 0xFFFFFFFF followed by 64-bit length
470+
# 00000000 ffffffff 0000000000000014 ffffffffffffffff CIE
471+
Content: FFFFFFFF1400000000000000FFFFFFFFFFFFFFFF03000178100C0708900100000000FFFFFFFF1800000000000000FFFFFFFFFFFFFFFF60020000000000000C00000000000000
472+
# DWARF64 FDE with invalid CIE pointer = 0xFFFFFFFFFFFFFFFF
473+
# Initial length: 0xFFFFFFFF, followed by 64-bit length (0x18)
474+
# Then 64-bit CIE pointer: 0xFFFFFFFFFFFFFFFF (which is DW64_CIE_ID, invalid for FDE)
475+
Symbols:
476+
- Name: test_invalid64
477+
Type: STT_FUNC
478+
Section: .text
479+
Value: 0x0000000000000260
480+
Size: 0x000000000000000C
481+
Binding: STB_GLOBAL
482+
...
483+
)");
484+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
485+
486+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
487+
SectionList *list = module_sp->GetSectionList();
488+
ASSERT_NE(nullptr, list);
489+
490+
auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false);
491+
ASSERT_NE(nullptr, section_sp);
492+
493+
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
494+
DWARFCallFrameInfo::DWARF);
495+
496+
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
497+
ConstString("test_invalid64"), eSymbolTypeAny);
498+
ASSERT_NE(nullptr, sym);
499+
500+
std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
501+
// The plan should be null because we have an invalid FDE
502+
EXPECT_EQ(nullptr, plan_up);
503+
}
504+
505+
// Test valid CIE markers in eh_frame format
506+
TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_eh_frame) {
507+
auto ExpectedFile = TestFile::fromYaml(R"(
508+
--- !ELF
509+
FileHeader:
510+
Class: ELFCLASS64
511+
Data: ELFDATA2LSB
512+
Type: ET_DYN
513+
Machine: EM_X86_64
514+
Entry: 0x0000000000000260
515+
Sections:
516+
- Name: .text
517+
Type: SHT_PROGBITS
518+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
519+
Address: 0x0000000000000260
520+
AddressAlign: 0x0000000000000010
521+
Content: 554889E5897DFC8B45FC5DC3
522+
- Name: .eh_frame
523+
Type: SHT_X86_64_UNWIND
524+
Flags: [ SHF_ALLOC ]
525+
Address: 0x0000000000000290
526+
AddressAlign: 0x0000000000000008
527+
# eh_frame content
528+
# CIE + FDE that works with address 0x260
529+
Content: 1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000
530+
Symbols:
531+
- Name: simple_function
532+
Type: STT_FUNC
533+
Section: .text
534+
Value: 0x0000000000000260
535+
Size: 0x000000000000000F
536+
Binding: STB_GLOBAL
537+
...
538+
)");
539+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
540+
541+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
542+
SectionList *list = module_sp->GetSectionList();
543+
ASSERT_NE(nullptr, list);
544+
545+
auto section_sp = list->FindSectionByType(eSectionTypeEHFrame, false);
546+
ASSERT_NE(nullptr, section_sp);
547+
548+
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
549+
DWARFCallFrameInfo::EH);
550+
551+
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
552+
ConstString("simple_function"), eSymbolTypeAny);
553+
ASSERT_NE(nullptr, sym);
554+
555+
std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
556+
// Should succeed with valid CIE and FDE
557+
ASSERT_NE(nullptr, plan_up);
558+
EXPECT_GE(plan_up->GetRowCount(), 1);
559+
}
560+
561+
// Test valid CIE markers in debug_frame DWARF32 format
562+
TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_dwarf32) {
563+
auto ExpectedFile = TestFile::fromYaml(R"(
564+
--- !ELF
565+
FileHeader:
566+
Class: ELFCLASS64
567+
Data: ELFDATA2LSB
568+
Type: ET_REL
569+
Machine: EM_X86_64
570+
Sections:
571+
- Name: .text
572+
Type: SHT_PROGBITS
573+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
574+
Address: 0x0000000000001130
575+
AddressAlign: 0x0000000000000010
576+
Content: 554889E5897DFC8B45FC83C0015DC3
577+
- Name: .debug_frame
578+
Type: SHT_PROGBITS
579+
AddressAlign: 0x0000000000000008
580+
# debug_frame content in DWARF32 format
581+
# CIE (length=0x14, CIE_id=0xFFFFFFFF, version=4)
582+
# FDE (length=0x24, CIE_offset=0)
583+
Content: 14000000FFFFFFFF040008000178100C0708900100000000240000000000000030110000000000000F00000000000000410E108602430D064A0C070800000000
584+
Symbols:
585+
- Name: simple_function
586+
Type: STT_FUNC
587+
Section: .text
588+
Value: 0x0000000000001130
589+
Size: 0x000000000000000F
590+
Binding: STB_GLOBAL
591+
...
592+
)");
593+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
594+
595+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
596+
SectionList *list = module_sp->GetSectionList();
597+
ASSERT_NE(nullptr, list);
598+
599+
auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false);
600+
ASSERT_NE(nullptr, section_sp);
601+
602+
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
603+
DWARFCallFrameInfo::DWARF);
604+
605+
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
606+
ConstString("simple_function"), eSymbolTypeAny);
607+
ASSERT_NE(nullptr, sym);
608+
609+
std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
610+
// Should succeed with valid CIE and FDE
611+
ASSERT_NE(nullptr, plan_up);
612+
EXPECT_GE(plan_up->GetRowCount(), 1);
613+
}
614+
615+
// Test valid CIE markers in debug_frame DWARF64 format
616+
TEST_F(DWARFCallFrameInfoTest, ValidCIEMarkers_dwarf64) {
617+
auto ExpectedFile = TestFile::fromYaml(R"(
618+
--- !ELF
619+
FileHeader:
620+
Class: ELFCLASS64
621+
Data: ELFDATA2LSB
622+
Type: ET_REL
623+
Machine: EM_X86_64
624+
Sections:
625+
- Name: .text
626+
Type: SHT_PROGBITS
627+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
628+
Address: 0x0000000000001130
629+
AddressAlign: 0x0000000000000010
630+
Content: 554889E5897DFC8B45FC83C0015DC3
631+
- Name: .debug_frame
632+
Type: SHT_PROGBITS
633+
AddressAlign: 0x0000000000000008
634+
# debug_frame content in DWARF64 format
635+
# CIE: length_marker=0xFFFFFFFF, length=0x14, CIE_id=0xFFFFFFFFFFFFFFFF, version=4
636+
# FDE: length_marker=0xFFFFFFFF, length=0x24, CIE_offset=0x0 (points to CIE)
637+
Content: FFFFFFFF1400000000000000FFFFFFFFFFFFFFFF040008000178100C07089001FFFFFFFF2400000000000000000000000000000030110000000000000F00000000000000410E108602430D064A0C0708
638+
Symbols:
639+
- Name: simple_function
640+
Type: STT_FUNC
641+
Section: .text
642+
Value: 0x0000000000001130
643+
Size: 0x000000000000000F
644+
Binding: STB_GLOBAL
645+
...
646+
)");
647+
ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
648+
649+
auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
650+
SectionList *list = module_sp->GetSectionList();
651+
ASSERT_NE(nullptr, list);
652+
653+
auto section_sp = list->FindSectionByType(eSectionTypeDWARFDebugFrame, false);
654+
ASSERT_NE(nullptr, section_sp);
655+
656+
DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
657+
DWARFCallFrameInfo::DWARF);
658+
659+
const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
660+
ConstString("simple_function"), eSymbolTypeAny);
661+
ASSERT_NE(nullptr, sym);
662+
663+
std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress());
664+
// Should succeed with valid CIE and FDE
665+
ASSERT_NE(nullptr, plan_up);
666+
EXPECT_GE(plan_up->GetRowCount(), 1);
667+
}

0 commit comments

Comments
 (0)