https://github.com/cpunion created https://github.com/llvm/llvm-project/pull/212597
## Summary Go's Darwin linker emits DWARF in Mach-O `__zdebug_*` sections by default. These sections use the GNU compression header (`ZLIB` followed by a big-endian 64-bit uncompressed size), while LLVM currently only handles the ELF compression header. This change: - recognizes GNU-style compressed Mach-O DWARF sections and normalizes their names for `DWARFContext`; - restores GNU compression header decoding in the shared object decompressor without changing ELF section detection; - makes LLDB decompress both full and partial reads of `__zdebug_*` sections. ## Testing - `ObjectFileMachOTests`: 30/30 on macOS, 28/28 on Linux arm64 - `llvm-lit llvm/test/DebugInfo/MachO/gnu-compressed-sections.yaml`: passed on macOS and Linux arm64, including the corrupt-header case - Real Go 1.26.5 Darwin executable with the default six `__zdebug_*` sections: `llvm-dwarfdump` reads `DW_LANG_Go` and the producer, and LLDB resolves a source breakpoint and reads parameters and locals >From 762d2160050504c264ebaf03d14dd17da1b05e9e Mon Sep 17 00:00:00 2001 From: Li Jie <[email protected]> Date: Tue, 28 Jul 2026 23:13:31 +0800 Subject: [PATCH 1/2] [llvm][DWARF] Support GNU-compressed sections in Mach-O --- llvm/include/llvm/Object/Decompressor.h | 1 + llvm/lib/DebugInfo/DWARF/DWARFContext.cpp | 6 +- llvm/lib/Object/Decompressor.cpp | 19 +++++- llvm/lib/Object/MachOObjectFile.cpp | 7 +- .../MachO/gnu-compressed-sections.yaml | 67 +++++++++++++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 llvm/test/DebugInfo/MachO/gnu-compressed-sections.yaml diff --git a/llvm/include/llvm/Object/Decompressor.h b/llvm/include/llvm/Object/Decompressor.h index 852b71ac86a55..362f66d540314 100644 --- a/llvm/include/llvm/Object/Decompressor.h +++ b/llvm/include/llvm/Object/Decompressor.h @@ -45,6 +45,7 @@ class Decompressor { private: Decompressor(StringRef Data); + Error consumeCompressedGNUHeader(); Error consumeCompressedHeader(bool Is64Bit, bool IsLittleEndian); StringRef SectionData; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index 70ce257a5804a..536dc75ba2836 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -2100,7 +2100,7 @@ class DWARFObjInMemory final : public DWARFObject { /// If Sec is compressed section, decompresses and updates its contents /// provided by Data. Otherwise leaves it unchanged. - Error maybeDecompress(const object::SectionRef &Sec, StringRef Name, + Error maybeDecompress(const object::SectionRef &Sec, StringRef &Name, StringRef &Data) { if (!Sec.isCompressed()) return Error::success(); @@ -2117,6 +2117,10 @@ class DWARFObjInMemory final : public DWARFObject { UncompressedSections.push_back(std::move(Out)); Data = UncompressedSections.back(); + StringRef NormalizedName = Name.ltrim("._"); + if (NormalizedName.starts_with("zdebug_")) + Name = NormalizedName.drop_front(); + return Error::success(); } diff --git a/llvm/lib/Object/Decompressor.cpp b/llvm/lib/Object/Decompressor.cpp index 9022566a97f35..826b9b6e0ec55 100644 --- a/llvm/lib/Object/Decompressor.cpp +++ b/llvm/lib/Object/Decompressor.cpp @@ -21,7 +21,9 @@ using namespace object; Expected<Decompressor> Decompressor::create(StringRef Name, StringRef Data, bool IsLE, bool Is64Bit) { Decompressor D(Data); - if (Error Err = D.consumeCompressedHeader(Is64Bit, IsLE)) + const bool IsGNUStyle = Name.ltrim("._").starts_with("zdebug_"); + if (Error Err = IsGNUStyle ? D.consumeCompressedGNUHeader() + : D.consumeCompressedHeader(Is64Bit, IsLE)) return std::move(Err); return D; } @@ -29,6 +31,21 @@ Expected<Decompressor> Decompressor::create(StringRef Name, StringRef Data, Decompressor::Decompressor(StringRef Data) : SectionData(Data), DecompressedSize(0) {} +Error Decompressor::consumeCompressedGNUHeader() { + if (!SectionData.starts_with("ZLIB")) + return createError("corrupted compressed section header"); + if (const char *Reason = + llvm::compression::getReasonIfUnsupported(compression::Format::Zlib)) + return createError(Reason); + if (SectionData.size() < 12) + return createError("corrupted uncompressed section size"); + + CompressionType = DebugCompressionType::Zlib; + DecompressedSize = read64be(SectionData.data() + 4); + SectionData = SectionData.substr(12); + return Error::success(); +} + Error Decompressor::consumeCompressedHeader(bool Is64Bit, bool IsLittleEndian) { using namespace ELF; uint64_t HdrSize = Is64Bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr); diff --git a/llvm/lib/Object/MachOObjectFile.cpp b/llvm/lib/Object/MachOObjectFile.cpp index 3342c99e245ca..f3c372b88caea 100644 --- a/llvm/lib/Object/MachOObjectFile.cpp +++ b/llvm/lib/Object/MachOObjectFile.cpp @@ -2057,7 +2057,12 @@ Expected<SectionRef> MachOObjectFile::getSection(StringRef SectionName) const { } bool MachOObjectFile::isSectionCompressed(DataRefImpl Sec) const { - return false; + Expected<StringRef> NameOrErr = getSectionName(Sec); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + return false; + } + return NameOrErr->ltrim("._").starts_with("zdebug_"); } bool MachOObjectFile::isSectionText(DataRefImpl Sec) const { diff --git a/llvm/test/DebugInfo/MachO/gnu-compressed-sections.yaml b/llvm/test/DebugInfo/MachO/gnu-compressed-sections.yaml new file mode 100644 index 0000000000000..0ddcc50ace1cd --- /dev/null +++ b/llvm/test/DebugInfo/MachO/gnu-compressed-sections.yaml @@ -0,0 +1,67 @@ +# REQUIRES: zlib +# RUN: yaml2obj %s -o %t \ +# RUN: -DINFO=5A4C4942000000000000000C789CE3606060606100010E0600009C0015 +# RUN: llvm-dwarfdump --debug-info %t | FileCheck %s +# RUN: yaml2obj %s -o %t.invalid \ +# RUN: -DINFO=4241442100000000000000000000000000000000000000000000000000 +# RUN: not llvm-dwarfdump --debug-info %t.invalid 2>&1 \ +# RUN: | FileCheck %s --check-prefix=ERR + +# CHECK: .debug_info contents: +# CHECK: Compile Unit: length = 0x00000008 +# CHECK-SAME: version = 0x0004 +# CHECK-SAME: addr_size = 0x08 + +# ERR: error: failed to decompress +# ERR-SAME: corrupted compressed section header + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x0100000C + cpusubtype: 0x00000000 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 232 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __DWARF + vmaddr: 0 + vmsize: 50 + fileoff: 264 + filesize: 50 + maxprot: 7 + initprot: 3 + nsects: 2 + flags: 0 + Sections: + - sectname: __zdebug_info + segname: __DWARF + addr: 0 + size: 29 + offset: 264 + align: 0 + reloff: 0 + nreloc: 0 + flags: 0x02000000 + reserved1: 0 + reserved2: 0 + reserved3: 0 + content: '[[INFO]]' + - sectname: __zdebug_abbrev + segname: __DWARF + addr: 29 + size: 21 + offset: 293 + align: 0 + reloff: 0 + nreloc: 0 + flags: 0x02000000 + reserved1: 0 + reserved2: 0 + reserved3: 0 + content: 5A4C49420000000000000001789C63000000010001 +... >From 9d591a7966054f44f9f690ef126f4f155d709a11 Mon Sep 17 00:00:00 2001 From: Li Jie <[email protected]> Date: Tue, 28 Jul 2026 23:41:34 +0800 Subject: [PATCH 2/2] [lldb][MachO] Decompress GNU-style DWARF sections --- .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 57 +++++++++++++++- .../ObjectFile/Mach-O/ObjectFileMachO.h | 7 ++ .../ObjectFile/MachO/TestObjectFileMachO.cpp | 67 +++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 9eab45f57f422..fb551248b27dc 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -32,6 +32,7 @@ #include "lldb/Target/ThreadList.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/LLDBLog.h" @@ -46,6 +47,7 @@ #include "lldb/Host/SafeMachO.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Object/Decompressor.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" @@ -1011,6 +1013,58 @@ bool ObjectFileMachO::ParseHeader(DataExtractorSP &extractor_sp, return false; } +size_t ObjectFileMachO::ReadSectionData(Section *section, + lldb::offset_t section_offset, + void *dst, size_t dst_len) { + if (section->GetObjectFile() != this) + return section->GetObjectFile()->ReadSectionData(section, section_offset, + dst, dst_len); + + if (!section->GetName().starts_with("__zdebug_")) + return ObjectFile::ReadSectionData(section, section_offset, dst, dst_len); + + DataExtractor data; + ReadSectionData(section, data); + return data.CopyData(section_offset, dst_len, dst); +} + +size_t ObjectFileMachO::ReadSectionData(Section *section, + DataExtractor §ion_data) { + if (section->GetObjectFile() != this) + return section->GetObjectFile()->ReadSectionData(section, section_data); + + size_t result = ObjectFile::ReadSectionData(section, section_data); + if (result == 0 || !section->GetName().starts_with("__zdebug_")) + return result; + + auto decompressor = llvm::object::Decompressor::create( + section->GetName(), + {reinterpret_cast<const char *>(section_data.GetDataStart()), + size_t(section_data.GetByteSize())}, + GetByteOrder() == eByteOrderLittle, GetAddressByteSize() == 8); + if (!decompressor) { + GetModule()->ReportWarning( + "unable to initialize decompressor for section '{0}': {1}", + section->GetName(), llvm::toString(decompressor.takeError()).c_str()); + section_data.Clear(); + return 0; + } + + auto buffer_sp = + std::make_shared<DataBufferHeap>(decompressor->getDecompressedSize(), 0); + if (auto error = decompressor->decompress( + {buffer_sp->GetBytes(), size_t(buffer_sp->GetByteSize())})) { + GetModule()->ReportWarning("decompression of section '{0}' failed: {1}", + section->GetName(), + llvm::toString(std::move(error)).c_str()); + section_data.Clear(); + return 0; + } + + section_data.SetData(buffer_sp); + return buffer_sp->GetByteSize(); +} + bool ObjectFileMachO::ParseHeader() { ModuleSP module_sp(GetModule()); if (!module_sp) @@ -1464,7 +1518,8 @@ static lldb::SectionType GetSectionType(uint32_t flags, return eSectionTypeDWARFDebugStrOffsetsDwo; llvm::StringRef stripped_name = section_name.GetStringRef(); - if (stripped_name.consume_front("__debug_")) + if (stripped_name.consume_front("__debug_") || + stripped_name.consume_front("__zdebug_")) return ObjectFile::GetDWARFSectionTypeFromName(stripped_name); if (section_name == g_sect_name_dwarf_apple_names) diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h index 4a0b9041b2f87..84605972e5d6a 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -102,6 +102,13 @@ class ObjectFileMachO : public lldb_private::ObjectFile { void CreateSections(lldb_private::SectionList &unified_section_list) override; + size_t ReadSectionData(lldb_private::Section *section, + lldb::offset_t section_offset, void *dst, + size_t dst_len) override; + + size_t ReadSectionData(lldb_private::Section *section, + lldb_private::DataExtractor §ion_data) override; + void Dump(lldb_private::Stream *s) override; lldb_private::ArchSpec GetArchitecture() override; diff --git a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp index b3a238022aa57..d90b342bf146c 100644 --- a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp +++ b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp @@ -19,6 +19,7 @@ #include "lldb/Symbol/Symtab.h" #include "lldb/Utility/FileSpec.h" #include "lldb/lldb-defines.h" +#include "llvm/Config/llvm-config.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -37,6 +38,72 @@ class ObjectFileMachOTest : public ::testing::Test { }; } // namespace +#if LLVM_ENABLE_ZLIB +TEST_F(ObjectFileMachOTest, GNUCompressedSection) { + const char *yamldata = R"( +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x0100000C + cpusubtype: 0x00000000 + filetype: 0x00000001 + ncmds: 1 + sizeofcmds: 152 + flags: 0x00000000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __DWARF + vmaddr: 0 + vmsize: 23 + fileoff: 184 + filesize: 23 + maxprot: 7 + initprot: 3 + nsects: 1 + flags: 0 + Sections: + - sectname: __zdebug_line + segname: __DWARF + addr: 0 + size: 23 + offset: 184 + align: 0 + reloff: 0 + nreloc: 0 + flags: 0x02000000 + reserved1: 0 + reserved2: 0 + reserved3: 0 + content: 5A4C49420000000000000003789C4B4C4A0600024D0127 +... +)"; + + llvm::Expected<TestFile> file = TestFile::fromYaml(yamldata); + ASSERT_THAT_EXPECTED(file, llvm::Succeeded()); + lldb::ModuleSP module = std::make_shared<Module>(file->moduleSpec()); + ObjectFile *object = module->GetObjectFile(); + ASSERT_TRUE(llvm::isa<ObjectFileMachO>(object)); + + SectionSP line = object->GetSectionList()->FindSectionByType( + eSectionTypeDWARFDebugLine, true); + ASSERT_TRUE(line); + EXPECT_EQ(line->GetName(), ConstString("__zdebug_line")); + + lldb_private::DataExtractor data; + EXPECT_EQ(line->GetSectionData(data), 3u); + ASSERT_EQ(data.GetByteSize(), 3u); + EXPECT_EQ( + llvm::StringRef(reinterpret_cast<const char *>(data.GetDataStart()), 3), + "abc"); + + char tail[2]; + EXPECT_EQ(object->ReadSectionData(line.get(), 1, tail, sizeof(tail)), 2u); + EXPECT_EQ(llvm::StringRef(tail, sizeof(tail)), "bc"); +} +#endif + #if defined(__APPLE__) TEST_F(ObjectFileMachOTest, ModuleFromSharedCacheInfo) { ArchSpec arch("arm64-apple-macosx-"); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
