From 794f706b3d2b82a261c7c55ecdb6bb2aa282e302 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 20 Apr 2026 11:03:50 +0200 Subject: [PATCH 1/6] Handle compressed DWARF sections in ELF binaries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/BinaryParsers/ElfBinary/ElfBinary.cs | 75 ++++++++++++++++++- .../Elf/ElfBinaryTests.cs | 33 ++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/BinaryParsers/ElfBinary/ElfBinary.cs b/src/BinaryParsers/ElfBinary/ElfBinary.cs index f38c14fda..8142a6184 100644 --- a/src/BinaryParsers/ElfBinary/ElfBinary.cs +++ b/src/BinaryParsers/ElfBinary/ElfBinary.cs @@ -2,8 +2,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Text; @@ -21,6 +23,11 @@ namespace Microsoft.CodeAnalysis.BinaryParsers /// public class ElfBinary : BinaryBase, IDwarfBinary { + // ELF section flag bit for SHF_COMPRESSED. + private const ulong CompressedSectionFlag = 0x800UL; + private const uint ZlibCompressionType = 1; + private const uint ZstdCompressionType = 2; + public ElfBinary(Uri uri, string localSymbolDirectories = null, bool forceComprehensiveParsing = false, @@ -393,7 +400,7 @@ private byte[] LoadSection(string sectionName) { if (section.Name == sectionName + ".dwo" || section.Name == sectionName) { - return section.GetContents(); + return GetSectionContents(section, Is64bit); } } @@ -407,7 +414,8 @@ private IDwarfStringReader CreateDebugStringsReader() .FirstOrDefault(candidate => candidate.Name == SectionName.DebugStr || candidate.Name == SectionName.DebugStr + ".dwo"); - if (section != null && ShouldUseFileBackedDwarfStringReader( + // File offsets cannot address strings inside a compressed section. + if (section != null && !IsCompressedSection(section) && ShouldUseFileBackedDwarfStringReader( section.Size, this.dwarfStringSectionFileReadThreshold)) { @@ -424,6 +432,69 @@ internal static bool ShouldUseFileBackedDwarfStringReader( return fileReadThreshold.HasValue && sectionSize >= fileReadThreshold.Value; } + internal static byte[] GetSectionContents(ISection section, bool is64bit) + { + byte[] contents = section.GetContents(); + + return IsCompressedSection(section) + ? DecompressSectionContents(contents, is64bit) + : contents; + } + + internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) + { + if (contents.Length == 0) + { + return contents; + } + + int headerSize = is64bit ? 24 : 12; + + if (contents.Length < headerSize) + { + throw new InvalidOperationException("Compressed ELF section header is truncated."); + } + + uint compressionType = BinaryPrimitives.ReadUInt32LittleEndian(contents.AsSpan(0, sizeof(uint))); + + if (compressionType == ZstdCompressionType) + { + throw new NotSupportedException("ELF zstd-compressed sections are not supported on this runtime target because there is no built-in Zstandard stream to decode them."); + } + + if (compressionType != ZlibCompressionType) + { + throw new NotSupportedException($"Unsupported ELF compression type: {compressionType}."); + } + + ulong uncompressedSize = is64bit + ? BinaryPrimitives.ReadUInt64LittleEndian(contents.AsSpan(8, sizeof(ulong))) + : BinaryPrimitives.ReadUInt32LittleEndian(contents.AsSpan(4, sizeof(uint))); + + using var compressedStream = new MemoryStream(contents, headerSize, contents.Length - headerSize, writable: false); + using var zlibStream = new ZLibStream(compressedStream, CompressionMode.Decompress); + using var decompressedStream = uncompressedSize <= int.MaxValue + ? new MemoryStream((int)uncompressedSize) + : new MemoryStream(); + + zlibStream.CopyTo(decompressedStream); + + byte[] decompressedContents = decompressedStream.ToArray(); + + if ((ulong)decompressedContents.LongLength != uncompressedSize) + { + throw new InvalidOperationException("Compressed ELF section size does not match the ELF header."); + } + + return decompressedContents; + } + + private static bool IsCompressedSection(ISection section) + { + return section is Section elfSection + && (elfSection.RawFlags & CompressedSectionFlag) != 0; + } + /// /// Gets the section address after loading into memory. /// diff --git a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs index 59d3e3fe9..6ad094ac9 100644 --- a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs +++ b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs @@ -4,8 +4,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Reflection; +using System.Text; using FluentAssertions; @@ -203,6 +205,37 @@ public void ValidateDwarfV5_Rust() binary.GetLanguage().Should().Be(DwarfLanguage.Rust); } + [Fact] + public void DecompressSectionContents_Elf64CompressedSection_Decompresses() + { + byte[] originalContents = Encoding.ASCII.GetBytes("dwarf-v5-go-section"); + + byte[] compressedContents; + using (var compressedStream = new MemoryStream()) + { + using (var zlibStream = new ZLibStream(compressedStream, CompressionLevel.SmallestSize, leaveOpen: true)) + { + zlibStream.Write(originalContents, 0, originalContents.Length); + } + + compressedContents = compressedStream.ToArray(); + } + + using var sectionStream = new MemoryStream(); + using (var writer = new BinaryWriter(sectionStream, Encoding.ASCII, leaveOpen: true)) + { + writer.Write((uint)1); + writer.Write((uint)0); + writer.Write((ulong)originalContents.Length); + writer.Write((ulong)1); + writer.Write(compressedContents); + } + + byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionStream.ToArray(), is64bit: true); + + decompressedContents.Should().Equal(originalContents); + } + [Fact] public void ValidateDwarfV4_WithO2_Split_DebugFileExists() { From 73b3cc270ba05324d1836bec569e2590093f0e97 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 20 Apr 2026 11:18:44 +0200 Subject: [PATCH 2/6] Fix format check for ELF decompression helper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/BinaryParsers/ElfBinary/ElfBinary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BinaryParsers/ElfBinary/ElfBinary.cs b/src/BinaryParsers/ElfBinary/ElfBinary.cs index 8142a6184..e3815275b 100644 --- a/src/BinaryParsers/ElfBinary/ElfBinary.cs +++ b/src/BinaryParsers/ElfBinary/ElfBinary.cs @@ -473,7 +473,7 @@ internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) using var compressedStream = new MemoryStream(contents, headerSize, contents.Length - headerSize, writable: false); using var zlibStream = new ZLibStream(compressedStream, CompressionMode.Decompress); - using var decompressedStream = uncompressedSize <= int.MaxValue + using MemoryStream decompressedStream = uncompressedSize <= int.MaxValue ? new MemoryStream((int)uncompressedSize) : new MemoryStream(); From db150e05f09bb4c6741abdecd1f096265df932c5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 18 May 2026 11:34:48 +0200 Subject: [PATCH 3/6] add elf32 test --- .../Elf/ElfBinaryTests.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs index 6ad094ac9..196c2440e 100644 --- a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs +++ b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs @@ -236,6 +236,36 @@ public void DecompressSectionContents_Elf64CompressedSection_Decompresses() decompressedContents.Should().Equal(originalContents); } + [Fact] + public void DecompressSectionContents_Elf32CompressedSection_Decompresses() + { + byte[] originalContents = Encoding.ASCII.GetBytes("dwarf-v5-go-section"); + + byte[] compressedContents; + using (var compressedStream = new MemoryStream()) + { + using (var zlibStream = new ZLibStream(compressedStream, CompressionLevel.SmallestSize, leaveOpen: true)) + { + zlibStream.Write(originalContents, 0, originalContents.Length); + } + + compressedContents = compressedStream.ToArray(); + } + + using var sectionStream = new MemoryStream(); + using (var writer = new BinaryWriter(sectionStream, Encoding.ASCII, leaveOpen: true)) + { + writer.Write((uint)1); + writer.Write((uint)originalContents.Length); + writer.Write((uint)1); + writer.Write(compressedContents); + } + + byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionStream.ToArray(), is64bit: false); + + decompressedContents.Should().Equal(originalContents); + } + [Fact] public void ValidateDwarfV4_WithO2_Split_DebugFileExists() { From 0bda53461006a0df97fda289e112c65a9a2a9c95 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Sep 2026 15:49:21 +0200 Subject: [PATCH 4/6] codereview feedback --- src/BinaryParsers/ElfBinary/ElfBinary.cs | 38 +++++-- .../Elf/ElfBinaryTests.cs | 105 ++++++++++++++---- 2 files changed, 113 insertions(+), 30 deletions(-) diff --git a/src/BinaryParsers/ElfBinary/ElfBinary.cs b/src/BinaryParsers/ElfBinary/ElfBinary.cs index e3815275b..abc7bbd42 100644 --- a/src/BinaryParsers/ElfBinary/ElfBinary.cs +++ b/src/BinaryParsers/ElfBinary/ElfBinary.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text; +using ELFSharp; using ELFSharp.ELF; using ELFSharp.ELF.Sections; using ELFSharp.ELF.Segments; @@ -400,7 +401,7 @@ private byte[] LoadSection(string sectionName) { if (section.Name == sectionName + ".dwo" || section.Name == sectionName) { - return GetSectionContents(section, Is64bit); + return GetSectionContents(section, Is64bit, ELF.Endianess); } } @@ -432,16 +433,16 @@ internal static bool ShouldUseFileBackedDwarfStringReader( return fileReadThreshold.HasValue && sectionSize >= fileReadThreshold.Value; } - internal static byte[] GetSectionContents(ISection section, bool is64bit) + internal static byte[] GetSectionContents(ISection section, bool is64bit, Endianess endianess) { byte[] contents = section.GetContents(); return IsCompressedSection(section) - ? DecompressSectionContents(contents, is64bit) + ? DecompressSectionContents(contents, is64bit, endianess) : contents; } - internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) + internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit, Endianess endianess) { if (contents.Length == 0) { @@ -455,7 +456,7 @@ internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) throw new InvalidOperationException("Compressed ELF section header is truncated."); } - uint compressionType = BinaryPrimitives.ReadUInt32LittleEndian(contents.AsSpan(0, sizeof(uint))); + uint compressionType = ReadUInt32(contents.AsSpan(0, sizeof(uint)), endianess); if (compressionType == ZstdCompressionType) { @@ -468,14 +469,17 @@ internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) } ulong uncompressedSize = is64bit - ? BinaryPrimitives.ReadUInt64LittleEndian(contents.AsSpan(8, sizeof(ulong))) - : BinaryPrimitives.ReadUInt32LittleEndian(contents.AsSpan(4, sizeof(uint))); + ? ReadUInt64(contents.AsSpan(8, sizeof(ulong)), endianess) + : ReadUInt32(contents.AsSpan(4, sizeof(uint)), endianess); + + if (uncompressedSize > (ulong)Array.MaxLength) + { + throw new InvalidDataException("Uncompressed ELF section size exceeds the maximum array length."); + } using var compressedStream = new MemoryStream(contents, headerSize, contents.Length - headerSize, writable: false); using var zlibStream = new ZLibStream(compressedStream, CompressionMode.Decompress); - using MemoryStream decompressedStream = uncompressedSize <= int.MaxValue - ? new MemoryStream((int)uncompressedSize) - : new MemoryStream(); + using var decompressedStream = new MemoryStream((int)uncompressedSize); zlibStream.CopyTo(decompressedStream); @@ -489,6 +493,20 @@ internal static byte[] DecompressSectionContents(byte[] contents, bool is64bit) return decompressedContents; } + private static uint ReadUInt32(ReadOnlySpan contents, Endianess endianess) + { + return endianess == Endianess.LittleEndian + ? BinaryPrimitives.ReadUInt32LittleEndian(contents) + : BinaryPrimitives.ReadUInt32BigEndian(contents); + } + + private static ulong ReadUInt64(ReadOnlySpan contents, Endianess endianess) + { + return endianess == Endianess.LittleEndian + ? BinaryPrimitives.ReadUInt64LittleEndian(contents) + : BinaryPrimitives.ReadUInt64BigEndian(contents); + } + private static bool IsCompressedSection(ISection section) { return section is Section elfSection diff --git a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs index 196c2440e..dfb2d9958 100644 --- a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs +++ b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.IO.Compression; @@ -9,6 +10,8 @@ using System.Reflection; using System.Text; +using ELFSharp; + using FluentAssertions; using Microsoft.CodeAnalysis.BinaryParsers.Dwarf; @@ -205,8 +208,10 @@ public void ValidateDwarfV5_Rust() binary.GetLanguage().Should().Be(DwarfLanguage.Rust); } - [Fact] - public void DecompressSectionContents_Elf64CompressedSection_Decompresses() + [Theory] + [InlineData(Endianess.LittleEndian)] + [InlineData(Endianess.BigEndian)] + public void DecompressSectionContents_Elf64CompressedSection_Decompresses(Endianess endianess) { byte[] originalContents = Encoding.ASCII.GetBytes("dwarf-v5-go-section"); @@ -221,23 +226,46 @@ public void DecompressSectionContents_Elf64CompressedSection_Decompresses() compressedContents = compressedStream.ToArray(); } - using var sectionStream = new MemoryStream(); - using (var writer = new BinaryWriter(sectionStream, Encoding.ASCII, leaveOpen: true)) + byte[] sectionContents = CreateCompressedSectionContents(compressedContents, (ulong)originalContents.Length, is64bit: true, endianess: endianess); + + byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionContents, is64bit: true, endianess: endianess); + + decompressedContents.Should().Equal(originalContents); + } + + [Theory] + [InlineData(Endianess.LittleEndian)] + [InlineData(Endianess.BigEndian)] + public void DecompressSectionContents_Elf32CompressedSection_Decompresses(Endianess endianess) + { + byte[] originalContents = Encoding.ASCII.GetBytes("dwarf-v5-go-section"); + + byte[] compressedContents; + using (var compressedStream = new MemoryStream()) { - writer.Write((uint)1); - writer.Write((uint)0); - writer.Write((ulong)originalContents.Length); - writer.Write((ulong)1); - writer.Write(compressedContents); + using (var zlibStream = new ZLibStream(compressedStream, CompressionLevel.SmallestSize, leaveOpen: true)) + { + zlibStream.Write(originalContents, 0, originalContents.Length); + } + + compressedContents = compressedStream.ToArray(); } - byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionStream.ToArray(), is64bit: true); + byte[] sectionContents = CreateCompressedSectionContents(compressedContents, (ulong)originalContents.Length, is64bit: false, endianess: endianess); + + byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionContents, is64bit: false, endianess: endianess); decompressedContents.Should().Equal(originalContents); } - [Fact] - public void DecompressSectionContents_Elf32CompressedSection_Decompresses() + [Theory] + [InlineData(false, Endianess.LittleEndian, false)] + [InlineData(false, Endianess.BigEndian, false)] + [InlineData(true, Endianess.LittleEndian, false)] + [InlineData(true, Endianess.BigEndian, false)] + [InlineData(true, Endianess.LittleEndian, true)] + [InlineData(true, Endianess.BigEndian, true)] + public void DecompressSectionContents_UncompressedSizeExceedsArrayMaxLength_Throws(bool is64bit, Endianess endianess, bool sizeExceeds32Bits) { byte[] originalContents = Encoding.ASCII.GetBytes("dwarf-v5-go-section"); @@ -252,18 +280,55 @@ public void DecompressSectionContents_Elf32CompressedSection_Decompresses() compressedContents = compressedStream.ToArray(); } - using var sectionStream = new MemoryStream(); - using (var writer = new BinaryWriter(sectionStream, Encoding.ASCII, leaveOpen: true)) + // Keep the low 32 bits equal to the payload length to catch truncated ELF64 size reads. + ulong uncompressedSize = sizeExceeds32Bits + ? (1UL << 32) + (ulong)originalContents.Length + : (ulong)Array.MaxLength + 1; + byte[] sectionContents = CreateCompressedSectionContents(compressedContents, uncompressedSize, is64bit, endianess); + + Action decompress = () => ElfBinary.DecompressSectionContents(sectionContents, is64bit, endianess); + + decompress.Should().Throw(); + } + + private static byte[] CreateCompressedSectionContents(byte[] compressedContents, ulong uncompressedSize, bool is64bit, Endianess endianess) + { + int headerSize = is64bit ? 24 : 12; + var contents = new byte[headerSize + compressedContents.Length]; + + if (endianess == Endianess.LittleEndian) { - writer.Write((uint)1); - writer.Write((uint)originalContents.Length); - writer.Write((uint)1); - writer.Write(compressedContents); + BinaryPrimitives.WriteUInt32LittleEndian(contents.AsSpan(0, sizeof(uint)), 1); + + if (is64bit) + { + BinaryPrimitives.WriteUInt64LittleEndian(contents.AsSpan(8, sizeof(ulong)), uncompressedSize); + BinaryPrimitives.WriteUInt64LittleEndian(contents.AsSpan(16, sizeof(ulong)), 1); + } + else + { + BinaryPrimitives.WriteUInt32LittleEndian(contents.AsSpan(4, sizeof(uint)), checked((uint)uncompressedSize)); + BinaryPrimitives.WriteUInt32LittleEndian(contents.AsSpan(8, sizeof(uint)), 1); + } } + else + { + BinaryPrimitives.WriteUInt32BigEndian(contents.AsSpan(0, sizeof(uint)), 1); - byte[] decompressedContents = ElfBinary.DecompressSectionContents(sectionStream.ToArray(), is64bit: false); + if (is64bit) + { + BinaryPrimitives.WriteUInt64BigEndian(contents.AsSpan(8, sizeof(ulong)), uncompressedSize); + BinaryPrimitives.WriteUInt64BigEndian(contents.AsSpan(16, sizeof(ulong)), 1); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(contents.AsSpan(4, sizeof(uint)), checked((uint)uncompressedSize)); + BinaryPrimitives.WriteUInt32BigEndian(contents.AsSpan(8, sizeof(uint)), 1); + } + } - decompressedContents.Should().Equal(originalContents); + compressedContents.CopyTo(contents, headerSize); + return contents; } [Fact] From a49c9f68e787a4ffcfed1790f0367287e90d642b Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 23 Sep 2026 16:35:16 +0200 Subject: [PATCH 5/6] fix formatting --- src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs index dfb2d9958..f53bb3665 100644 --- a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs +++ b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs @@ -294,7 +294,7 @@ public void DecompressSectionContents_UncompressedSizeExceedsArrayMaxLength_Thro private static byte[] CreateCompressedSectionContents(byte[] compressedContents, ulong uncompressedSize, bool is64bit, Endianess endianess) { int headerSize = is64bit ? 24 : 12; - var contents = new byte[headerSize + compressedContents.Length]; + byte[] contents = new byte[headerSize + compressedContents.Length]; if (endianess == Endianess.LittleEndian) { From e2d93a3efd1cebd7844401034687914f42b51a2e Mon Sep 17 00:00:00 2001 From: qmuntal Date: Thu, 24 Sep 2026 11:04:04 +0200 Subject: [PATCH 6/6] add some real compressed ELF fixures --- .../Elf/ElfBinaryTests.cs | 40 ++++++++++++++++++ .../TestData/Dwarf/hello-dwarf4-o2-compressed | Bin 0 -> 18272 bytes .../TestData/Dwarf/hello-dwarf5-o2-compressed | Bin 0 -> 18224 bytes 3 files changed, 40 insertions(+) create mode 100644 src/Test.UnitTests.BinaryParsers/TestData/Dwarf/hello-dwarf4-o2-compressed create mode 100644 src/Test.UnitTests.BinaryParsers/TestData/Dwarf/hello-dwarf5-o2-compressed diff --git a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs index f53bb3665..62ccca04e 100644 --- a/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs +++ b/src/Test.UnitTests.BinaryParsers/Elf/ElfBinaryTests.cs @@ -11,6 +11,7 @@ using System.Text; using ELFSharp; +using ELFSharp.ELF.Sections; using FluentAssertions; @@ -197,6 +198,45 @@ public void ValidateDwarfV5_WithO2() binary.GetLanguage().Should().Be(DwarfLanguage.C11); } + [Theory] + [InlineData("hello-dwarf4-o2-compressed", 4, DwarfLanguage.C99, null)] + [InlineData("hello-dwarf4-o2-compressed", 4, DwarfLanguage.C99, 0UL)] + [InlineData("hello-dwarf5-o2-compressed", 5, DwarfLanguage.C11, null)] + [InlineData("hello-dwarf5-o2-compressed", 5, DwarfLanguage.C11, 0UL)] + public void ValidateDwarf_WithCompressedSections( + string fileName, + int expectedVersion, + DwarfLanguage expectedLanguage, + ulong? fileReadThreshold) + { + // Compressed copies of the existing ELF fixtures, generated with GNU binutils 2.42: + // x86_64-linux-gnu-objcopy --compress-debug-sections=zlib-gabi hello-dwarf4-o2 hello-dwarf4-o2-compressed + // x86_64-linux-gnu-objcopy --compress-debug-sections=zlib-gabi hello-dwarf5-o2 hello-dwarf5-o2-compressed + string filePath = Path.Combine(TestData, "Dwarf", fileName); + using var binary = new ElfBinary(new Uri(filePath), dwarfStringSectionFileReadThreshold: fileReadThreshold); + string originalFilePath = Path.Combine(TestData, "Dwarf", $"hello-dwarf{expectedVersion}-o2"); + using var originalBinary = new ElfBinary(new Uri(originalFilePath)); + + binary.LoadException.Should().BeNull(); + binary.Valid.Should().BeTrue(); + binary.ELF.Sections + .OfType>() + .Where(section => section.Name == ".debug_info" || section.Name == ".debug_str") + .Should().HaveCount(2) + .And.OnlyContain(section => (section.RawFlags & 0x800UL) != 0); // SHF_COMPRESSED + + // A zero threshold must still decode compressed strings instead of reading raw file offsets. + binary.DwarfVersion.Should().Be(expectedVersion); + binary.DebugFileType.Should().Be(DebugFileType.DebugIncluded); + binary.DebugFileLoaded.Should().BeTrue(); + binary.GetLanguage().Should().Be(expectedLanguage); + binary.CommandLineInfos + .Where(info => info.Language != DwarfLanguage.Unknown) + .Should().NotBeEmpty() + .And.OnlyContain(info => info.CommandLine.Contains("O2")); + binary.DebugLine.Should().NotBeEmpty().And.Equal(originalBinary.DebugLine); + } + [Fact] public void ValidateDwarfV5_Rust() { diff --git a/src/Test.UnitTests.BinaryParsers/TestData/Dwarf/hello-dwarf4-o2-compressed b/src/Test.UnitTests.BinaryParsers/TestData/Dwarf/hello-dwarf4-o2-compressed new file mode 100644 index 0000000000000000000000000000000000000000..9430e1c5b48e5b90a045b42a445eb30ab8aeed9e GIT binary patch literal 18272 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 literal 0 HcmV?d00001