https://github.com/royitaqi updated https://github.com/llvm/llvm-project/pull/196448
>From b0171e9428db3628823a1de6bbb657f99df32075 Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Thu, 7 May 2026 16:36:21 -0700 Subject: [PATCH 1/6] Add a test to repro the problem --- llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp | 156 +++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp index 299e19e3c896e..b7abdd94ce645 100644 --- a/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp +++ b/llvm/unittests/DebugInfo/GSYM/GSYMTest.cpp @@ -9,6 +9,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h" #include "llvm/DebugInfo/GSYM/DwarfTransformer.h" #include "llvm/DebugInfo/GSYM/ExtractRanges.h" #include "llvm/DebugInfo/GSYM/FileEntry.h" @@ -5687,3 +5688,158 @@ TEST(GSYMTest, TestMergedFunctionsInfoLargeOffsets) { EXPECT_EQ(DecResult->MergedFunctions[0].Name, LargeName1); EXPECT_EQ(DecResult->MergedFunctions[1].Name, LargeName2); } + +TEST(GSYMTest, TestDWARFTypedefCycleDoesNotCrash) { + // Test that a self-referencing typedef cycle in DWARF does not cause + // infinite recursion in DWARFTypePrinter::unwrapReferencedTypedefType(). + // This can happen when dsymutil's classic linker incorrectly deduplicates + // typedefs with the same name but different underlying types (e.g. from + // preferred_name), creating a typedef that points to itself. + // + // The crash path: DWARFTypePrinter::appendUnqualifiedNameBefore sees a + // DW_AT_name with the _STN| prefix (simplified template name), calls + // appendTemplateParameters, which for each DW_TAG_template_type_parameter + // calls unwrapReferencedTypedefType. With a cyclic typedef, this recurses + // infinitely. + // + // debug_info layout (DWARF32, AddrSize=8): + // 0x00: unit_length (4 bytes) + // 0x04: version=4 (2), abbrev_offset (4), addr_size=8 (1) = 7 bytes + // 0x0B: CU DIE (abbrev 1): strp(4) + addr(8) + addr(8) + data2(2) = 23 + // 0x22: Subprogram DIE (abbrev 2): strp(4) + addr(8) + addr(8) = 21 + // 0x37: Template param DIE (abbrev 3): strp(4) + ref4(4) = 9 + // 0x40: null terminator (1 byte, end of subprogram children) + // 0x41: Typedef DIE (abbrev 4): strp(4) + ref4(4) = 9 + // 0x4A: null terminator (1 byte, end of CU children) + // + // Template param's DW_AT_type -> 0x41 (typedef) + // Typedef's DW_AT_type -> 0x41 (self-referencing cycle) + + // String table: "" (0x00), "/tmp/main.cpp" (0x01), "_STN|foo|<MyType>" + // (0x0F), "T" (0x21), "MyType" (0x23) + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.cpp + - '_STN|foo|<MyType>' + - T + - MyType + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Code: 0x00000003 + Tag: DW_TAG_template_type_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + - Code: 0x00000004 + Tag: DW_TAG_typedef + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_type + Form: DW_FORM_ref4 + debug_info: + - Version: 4 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - Value: 0x0000000000000004 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000F + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000003 + Values: + - Value: 0x0000000000000021 + - Value: 0x0000000000000041 + - AbbrCode: 0x00000000 + - AbbrCode: 0x00000004 + Values: + - Value: 0x0000000000000023 + - Value: 0x0000000000000041 + - AbbrCode: 0x00000000 + )"; + auto ErrOrSections = DWARFYAML::emitDebugSections(yamldata); + ASSERT_THAT_EXPECTED(ErrOrSections, Succeeded()); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + ASSERT_TRUE(DwarfContext.get() != nullptr); + + // Verify the typedef DIE is at offset 0x41 and self-references. + auto &CUDie = *DwarfContext->compile_units().begin(); + DWARFDie CURoot = CUDie->getUnitDIE(false); + ASSERT_TRUE(CURoot.isValid()); + // Walk children to find the typedef and verify the cycle. + bool FoundTypedef = false; + for (DWARFDie Child : CURoot.children()) { + if (Child.getTag() == dwarf::DW_TAG_typedef) { + EXPECT_EQ(Child.getOffset(), 0x41u); + auto TypeAttr = Child.find(dwarf::DW_AT_type); + ASSERT_TRUE(TypeAttr.has_value()); + auto RefDie = Child.getAttributeValueAsReferencedDie(*TypeAttr); + EXPECT_EQ(RefDie.getOffset(), Child.getOffset()); + FoundTypedef = true; + } + } + ASSERT_TRUE(FoundTypedef); + + // Exercise DWARFTypePrinter on the subprogram with the _STN| name. + // appendUnqualifiedName -> appendTemplateParameters -> + // unwrapReferencedTypedefType must not infinitely recurse. + for (DWARFDie Child : CURoot.children()) { + if (Child.getTag() == dwarf::DW_TAG_subprogram) { + std::string Result; + raw_string_ostream StrOS(Result); + DWARFTypePrinter<DWARFDie>(StrOS).appendUnqualifiedName(Child); + EXPECT_FALSE(Result.empty()); + } + } + + // Also verify DwarfTransformer::convert() succeeds. + auto &OS = llvm::nulls(); + OutputAggregator OSAgg(&OS); + GsymCreatorV1 GC; + DwarfTransformer DT(*DwarfContext, GC); + ASSERT_THAT_ERROR(DT.convert(1, OSAgg), Succeeded()); + ASSERT_THAT_ERROR(GC.finalize(OSAgg), Succeeded()); + SmallString<512> Str; + raw_svector_ostream OutStrm(Str); + FileWriter FW(OutStrm, llvm::endianness::native); + FW.setStringOffsetSize(GC.getStringOffsetSize()); + ASSERT_THAT_ERROR(GC.encode(FW), Succeeded()); + auto GROrErr = GsymReader::copyBuffer(OutStrm.str()); + ASSERT_THAT_EXPECTED(GROrErr, Succeeded()); + const std::unique_ptr<GsymReader> &GR = *GROrErr; + EXPECT_EQ(GR->getNumAddresses(), 1u); +} >From 764b1e4f0fd7cc21fa2a17e309cabdaf4439108e Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Thu, 7 May 2026 16:36:33 -0700 Subject: [PATCH 2/6] Fix the problem --- .../include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h index 9986aaabf6ed4..ccb823f95b74a 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h @@ -147,6 +147,8 @@ void DWARFTypePrinter<DieType>::appendArrayType(const DieType &D) { EndedWithTemplate = false; } +constexpr unsigned kMaxTypedefUnwrapDepth = 256; + namespace detail { template <typename DieType> DieType resolveReferencedType(DieType D, @@ -169,8 +171,11 @@ const char *toString(std::optional<DWARFFormValueType> F) { } /// Resolve the DW_AT_type of \c D until we reach a DIE that is not a -/// DW_TAG_typedef. -template <typename DieType> DieType unwrapReferencedTypedefType(DieType D) { +/// DW_TAG_typedef. Gives up after 256 typedefs to guard against cycles in +/// malformed DWARF. +template <typename DieType> +DieType unwrapReferencedTypedefType(DieType D, + unsigned Depth = kMaxTypedefUnwrapDepth) { auto TypeAttr = D.find(dwarf::DW_AT_type); if (!TypeAttr) return DieType(); @@ -179,8 +184,8 @@ template <typename DieType> DieType unwrapReferencedTypedefType(DieType D) { if (!Unwrapped) return DieType(); - if (Unwrapped.getTag() == dwarf::DW_TAG_typedef) - return unwrapReferencedTypedefType(Unwrapped); + if (Unwrapped.getTag() == dwarf::DW_TAG_typedef && Depth > 0) + return unwrapReferencedTypedefType(Unwrapped, Depth - 1); return Unwrapped; } >From bae699aba48cad7e46b502c5374b7703177be755 Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Thu, 7 May 2026 17:07:34 -0700 Subject: [PATCH 3/6] Inline the magic number --- llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h index ccb823f95b74a..0310ffc894e27 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h @@ -147,8 +147,6 @@ void DWARFTypePrinter<DieType>::appendArrayType(const DieType &D) { EndedWithTemplate = false; } -constexpr unsigned kMaxTypedefUnwrapDepth = 256; - namespace detail { template <typename DieType> DieType resolveReferencedType(DieType D, @@ -174,8 +172,7 @@ const char *toString(std::optional<DWARFFormValueType> F) { /// DW_TAG_typedef. Gives up after 256 typedefs to guard against cycles in /// malformed DWARF. template <typename DieType> -DieType unwrapReferencedTypedefType(DieType D, - unsigned Depth = kMaxTypedefUnwrapDepth) { +DieType unwrapReferencedTypedefType(DieType D, unsigned Depth = 256) { auto TypeAttr = D.find(dwarf::DW_AT_type); if (!TypeAttr) return DieType(); >From 3fce4a2f81426c32b1cf8f8c4de2550b9df216ec Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Fri, 8 May 2026 12:14:55 -0700 Subject: [PATCH 4/6] Convert to loop; use DenseSet --- .../llvm/DebugInfo/DWARF/DWARFTypePrinter.h | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h index 0310ffc894e27..4d97ac93eea1b 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h @@ -9,6 +9,7 @@ #ifndef LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H #define LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" @@ -169,22 +170,24 @@ const char *toString(std::optional<DWARFFormValueType> F) { } /// Resolve the DW_AT_type of \c D until we reach a DIE that is not a -/// DW_TAG_typedef. Gives up after 256 typedefs to guard against cycles in -/// malformed DWARF. -template <typename DieType> -DieType unwrapReferencedTypedefType(DieType D, unsigned Depth = 256) { - auto TypeAttr = D.find(dwarf::DW_AT_type); - if (!TypeAttr) - return DieType(); +/// DW_TAG_typedef. Gives up if a cycle is detected in malformed DWARF. +/// In this case, return the last typedef DIE before the cycle is formed. +template <typename DieType> DieType unwrapReferencedTypedefType(DieType D) { + SmallSet<uint64_t, 4> Visited; + while (true) { + auto TypeAttr = D.find(dwarf::DW_AT_type); + if (!TypeAttr) + return DieType(); - auto Unwrapped = detail::resolveReferencedType(D, *TypeAttr); - if (!Unwrapped) - return DieType(); + auto Unwrapped = detail::resolveReferencedType(D, *TypeAttr); + if (!Unwrapped || Unwrapped.getTag() != dwarf::DW_TAG_typedef) + return Unwrapped; - if (Unwrapped.getTag() == dwarf::DW_TAG_typedef && Depth > 0) - return unwrapReferencedTypedefType(Unwrapped, Depth - 1); + if (!Visited.insert(Unwrapped.getOffset()).second) + return D; - return Unwrapped; + D = Unwrapped; + } } } // namespace detail >From dfd225752832e5b498b7105c8ce6c0a33f3abbc9 Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Fri, 8 May 2026 12:21:37 -0700 Subject: [PATCH 5/6] Instead, return the DIE where the cycle is formed --- llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h index 4d97ac93eea1b..6c46d7cde77ef 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h @@ -171,7 +171,7 @@ const char *toString(std::optional<DWARFFormValueType> F) { /// Resolve the DW_AT_type of \c D until we reach a DIE that is not a /// DW_TAG_typedef. Gives up if a cycle is detected in malformed DWARF. -/// In this case, return the last typedef DIE before the cycle is formed. +/// In this case, returns the typedef DIE where the cycle is formed. template <typename DieType> DieType unwrapReferencedTypedefType(DieType D) { SmallSet<uint64_t, 4> Visited; while (true) { @@ -184,7 +184,7 @@ template <typename DieType> DieType unwrapReferencedTypedefType(DieType D) { return Unwrapped; if (!Visited.insert(Unwrapped.getOffset()).second) - return D; + return Unwrapped; D = Unwrapped; } >From eaf2082ce64dc0676d6f519acc5c7937187fb168 Mon Sep 17 00:00:00 2001 From: Roy Shi <[email protected]> Date: Fri, 8 May 2026 12:52:34 -0700 Subject: [PATCH 6/6] Fix build error in lldb --- lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h index d92de658a49e8..9736b80ac375f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h @@ -123,6 +123,8 @@ class DWARFBaseDIE { // LLVM libraries. dw_tag_t getTag() const { return Tag(); } + dw_offset_t getOffset() const { return GetOffset(); } + const char *getShortName() const { return GetName(); } protected: _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
