From 0bf36047e8317e98b51d223ef187c368aaf78cfa Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 4 Oct 2023 18:58:54 +0200 Subject: [PATCH 1/6] Add support for more than SHN_LORESERVE (0xff00) sections --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 30 ++++++ src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 97 +++++++++++++++++-- src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs | 37 +++++-- .../Elf/Sections/ElfBinarySection.cs | 2 +- 4 files changed, 148 insertions(+), 18 deletions(-) diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 658975d..7b9a8b5 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -421,5 +421,35 @@ public void TestAlignedSection() Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Offset, "Invalid alignment"); } + + [Test] + public void TestManySections() + { + var elf = new ElfObjectFile(ElfArch.X86_64); + + for (int i = 0; i < ushort.MaxValue; i++) + { + elf.AddSection(new ElfBinarySection { Name = $".section{i}" }); + } + elf.AddSection(new ElfSectionHeaderStringTable()); + + var diagnostics = elf.Verify(); + Assert.False(diagnostics.HasErrors); + + uint visibleSectionCount = elf.VisibleSectionCount; + + using (var outStream = File.OpenWrite("manysections")) + { + elf.Write(outStream); + outStream.Flush(); + } + + using (var inStream = File.OpenRead("manysections")) + { + elf = ElfObjectFile.Read(inStream); + } + + Assert.AreEqual(visibleSectionCount, elf.VisibleSectionCount); + } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index 6f5edec..35fb0ff 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using static System.Collections.Specialized.BitVector32; namespace LibObjectFile.Elf { @@ -17,8 +18,8 @@ internal abstract class ElfReader : ElfReader where TDecoder : struct, private TDecoder _decoder; private ulong _startOfFile; private ushort _programHeaderCount; - private ushort _sectionHeaderCount; - private ushort _sectionStringTableIndex; + private uint _sectionHeaderCount; + private uint _sectionStringTableIndex; private bool _isFirstSectionValidNull; private bool _hasValidSectionStringTable; @@ -193,7 +194,7 @@ private ElfSegment ReadProgramHeader64(int phdrIndex) private void ReadSections() { - if (_sectionHeaderCount == 0) return; + if (Layout.OffsetOfSectionHeaderTable == 0) return; // Write section header table ReadSectionHeaderTable(); @@ -210,9 +211,25 @@ private void ReadSectionHeaderTable() return; } - for (int i = 0; i < _sectionHeaderCount; i++) + uint i = 0; + + if (_sectionHeaderCount == 0) { - var offset = Layout.OffsetOfSectionHeaderTable + (ulong)i * Layout.SizeOfSectionHeaderEntry; + // We are dealing with an object file that has more than SHN_LORESERVE + // (0xff00) sections. It has to begin with a NULL section header where + // its Size contains the real number of sections, and Link optionally + // points to string table section if it's section index is too high. + if (ReadExtendedNullSectionTableEntry()) + { + i = 1; + ObjectFile.AddSection(new ElfNullSection()); + _isFirstSectionValidNull = true; + } + } + + for (; i < _sectionHeaderCount; i++) + { + var offset = Layout.OffsetOfSectionHeaderTable + i * Layout.SizeOfSectionHeaderEntry; if (offset >= (ulong)Stream.Length) { @@ -228,12 +245,12 @@ private void ReadSectionHeaderTable() } } - private ElfSection ReadSectionTableEntry(int sectionIndex) + private ElfSection ReadSectionTableEntry(uint sectionIndex) { return ObjectFile.FileClass == ElfFileClass.Is32 ? ReadSectionTableEntry32(sectionIndex) : ReadSectionTableEntry64(sectionIndex); } - private ElfSection ReadSectionTableEntry32(int sectionIndex) + private ElfSection ReadSectionTableEntry32(uint sectionIndex) { var streamOffset = Stream.Position; if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection)) @@ -267,7 +284,7 @@ private ElfSection ReadSectionTableEntry32(int sectionIndex) return section; } - private ElfSection ReadSectionTableEntry64(int sectionIndex) + private ElfSection ReadSectionTableEntry64(uint sectionIndex) { var streamOffset = Stream.Position; if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection)) @@ -300,6 +317,68 @@ private ElfSection ReadSectionTableEntry64(int sectionIndex) return section; } + + private bool ReadExtendedNullSectionTableEntry() + { + uint sh_type; + ulong sh_size; + uint sh_link; + bool isNull; + + Stream.Position = (long)Layout.OffsetOfSectionHeaderTable; + + if (ObjectFile.FileClass == ElfFileClass.Is32) + { + + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection32)) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader32Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); + return false; + } + + sh_type = _decoder.Decode(rawSection32.sh_type); + sh_size = _decoder.Decode(rawSection32.sh_size); + sh_link = _decoder.Decode(rawSection32.sh_link); + rawSection32.sh_size = 0; + rawSection32.sh_link = 0; + isNull = rawSection32.IsNull; + } + else + { + if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection64)) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_IncompleteSectionHeader64Size, $"Unable to read entirely NULL section header. Not enough data (size: {Layout.SizeOfSectionHeaderEntry}) read at offset {Layout.OffsetOfSectionHeaderTable} from the stream"); + return false; + } + + sh_type = _decoder.Decode(rawSection64.sh_type); + sh_size = _decoder.Decode(rawSection64.sh_size); + sh_link = _decoder.Decode(rawSection64.sh_link); + rawSection64.sh_size = 0; + rawSection64.sh_link = 0; + isNull = rawSection64.IsNull; + } + + if (!isNull) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {(ElfSectionType)sh_type}. Expecting {ElfNative.SHN_UNDEF}"); + return false; + } + + if (sh_size >= uint.MaxValue) + { + Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Extended section count [{sh_size}] exceeds {uint.MaxValue}"); + return false; + } + + _sectionHeaderCount = (uint)sh_size; + if (_sectionStringTableIndex == ElfNative.SHN_XINDEX) + { + _sectionStringTableIndex = sh_link; + } + + return true; + } public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat) { @@ -609,7 +688,7 @@ private void VerifyAndFixProgramHeadersAndSections() } } - private ElfSection CreateElfSection(int sectionIndex, ElfSectionType sectionType, bool isNullSection) + private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionType, bool isNullSection) { ElfSection section = null; diff --git a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs index 695195d..de3c122 100644 --- a/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs +++ b/src/LibObjectFile/Elf/ElfWriter{TEncoder}.cs @@ -155,8 +155,9 @@ private unsafe void WriteSectionHeader32() // entries for sections _encoder.Encode(out hdr.e_shoff, (uint)Layout.OffsetOfSectionHeaderTable); _encoder.Encode(out hdr.e_shentsize, Layout.SizeOfSectionHeaderEntry); - _encoder.Encode(out hdr.e_shnum, (ushort)ObjectFile.VisibleSectionCount); - _encoder.Encode(out hdr.e_shstrndx, (ushort)(ObjectFile.SectionHeaderStringTable?.SectionIndex ?? (ushort)0)); + _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); Write(hdr); } @@ -181,11 +182,13 @@ private unsafe void WriteSectionHeader64() // entries for sections _encoder.Encode(out hdr.e_shoff, Layout.OffsetOfSectionHeaderTable); _encoder.Encode(out hdr.e_shentsize, (ushort)sizeof(Elf64_Shdr)); - _encoder.Encode(out hdr.e_shnum, (ushort)ObjectFile.VisibleSectionCount); - _encoder.Encode(out hdr.e_shstrndx, (ushort)(ObjectFile.SectionHeaderStringTable?.SectionIndex ?? (ushort)0)); + _encoder.Encode(out hdr.e_shnum, ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE ? (ushort)0 : (ushort)ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out hdr.e_shstrndx, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? (ushort)ElfNative.SHN_XINDEX : (ushort)shstrSectionIndex); Write(hdr); } + private void CheckProgramHeaders() { if (ObjectFile.Segments.Count == 0) @@ -268,8 +271,17 @@ private void WriteSectionTableEntry32(ElfSection section) _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); _encoder.Encode(out shdr.sh_addr, (uint)section.VirtualAddress); _encoder.Encode(out shdr.sh_offset, (uint)section.Offset); - _encoder.Encode(out shdr.sh_size, (uint)section.Size); - _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); + if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) + { + _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); + } + else + { + _encoder.Encode(out shdr.sh_size, (uint)section.Size); + _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); + } _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); _encoder.Encode(out shdr.sh_addralign, (uint)section.Alignment); _encoder.Encode(out shdr.sh_entsize, (uint)section.TableEntrySize); @@ -284,8 +296,17 @@ private void WriteSectionTableEntry64(ElfSection section) _encoder.Encode(out shdr.sh_flags, (uint)section.Flags); _encoder.Encode(out shdr.sh_addr, section.VirtualAddress); _encoder.Encode(out shdr.sh_offset, section.Offset); - _encoder.Encode(out shdr.sh_size, section.Size); - _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); + if (section.Index == 0 && ObjectFile.VisibleSectionCount >= ElfNative.SHN_LORESERVE) + { + _encoder.Encode(out shdr.sh_size, ObjectFile.VisibleSectionCount); + uint shstrSectionIndex = ObjectFile.SectionHeaderStringTable?.SectionIndex ?? 0u; + _encoder.Encode(out shdr.sh_link, shstrSectionIndex >= ElfNative.SHN_LORESERVE ? shstrSectionIndex : 0); + } + else + { + _encoder.Encode(out shdr.sh_size, section.Size); + _encoder.Encode(out shdr.sh_link, section.Link.GetIndex()); + } _encoder.Encode(out shdr.sh_info, section.Info.GetIndex()); _encoder.Encode(out shdr.sh_addralign, section.Alignment); _encoder.Encode(out shdr.sh_entsize, section.TableEntrySize); diff --git a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs index 4aeaf7b..db864d9 100644 --- a/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs +++ b/src/LibObjectFile/Elf/Sections/ElfBinarySection.cs @@ -42,7 +42,7 @@ public override ElfSectionType Type } public override ulong TableEntrySize => OriginalTableEntrySize; - + /// /// Gets or sets the associated stream to this section. /// From 9d69b892e354e465e811816b6e2a1688cd064bff Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 11 Oct 2023 12:08:23 +0200 Subject: [PATCH 2/6] Test section names. --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 7b9a8b5..10c7588 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -450,6 +450,14 @@ public void TestManySections() } Assert.AreEqual(visibleSectionCount, elf.VisibleSectionCount); + Assert.True(elf.Sections[0] is ElfNullSection); + Assert.True(elf.Sections[1] is ElfProgramHeaderTable); + + for (int i = 0; i < ushort.MaxValue; i++) + { + Assert.True(elf.Sections[i + 2] is ElfBinarySection); + Assert.AreEqual($".section{i}", elf.Sections[i + 2].Name.Value); + } } } } \ No newline at end of file From 1746b62eba06629d82b683dd0d7b17d404165e51 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 11 Oct 2023 23:46:17 +0200 Subject: [PATCH 3/6] Add support for symbol table referencing large section indexes --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 19 ++- src/LibObjectFile/DiagnosticId.cs | 2 + src/LibObjectFile/Elf/ElfObjectFile.cs | 6 + src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 23 +++- .../Elf/Sections/ElfSymbolTable.cs | 40 ++++++- .../ElfSymbolTableSectionHeaderIndices.cs | 112 ++++++++++++++++++ 6 files changed, 190 insertions(+), 12 deletions(-) create mode 100644 src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 10c7588..9877561 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -426,16 +426,33 @@ public void TestAlignedSection() public void TestManySections() { var elf = new ElfObjectFile(ElfArch.X86_64); + var stringTable = new ElfStringTable(); + var symbolTable = new ElfSymbolTable { Link = stringTable }; for (int i = 0; i < ushort.MaxValue; i++) { - elf.AddSection(new ElfBinarySection { Name = $".section{i}" }); + var section = new ElfBinarySection { Name = $".section{i}" }; + elf.AddSection(section); + symbolTable.Entries.Add(new ElfSymbol { Type = ElfSymbolType.Section, Section = section }); } + + elf.AddSection(stringTable); + elf.AddSection(symbolTable); elf.AddSection(new ElfSectionHeaderStringTable()); var diagnostics = elf.Verify(); + Assert.True(diagnostics.HasErrors); + Assert.AreEqual(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, diagnostics.Messages[0].Id); + + elf.AddSection(new ElfSymbolTableSectionHeaderIndices { Link = symbolTable }); + diagnostics = elf.Verify(); Assert.False(diagnostics.HasErrors); + for (int i = 0; i < ushort.MaxValue; i++) + { + + } + uint visibleSectionCount = elf.VisibleSectionCount; using (var outStream = File.OpenWrite("manysections")) diff --git a/src/LibObjectFile/DiagnosticId.cs b/src/LibObjectFile/DiagnosticId.cs index 3c7b48e..170dd84 100644 --- a/src/LibObjectFile/DiagnosticId.cs +++ b/src/LibObjectFile/DiagnosticId.cs @@ -69,6 +69,8 @@ public enum DiagnosticId ELF_ERR_InvalidStreamForSectionNoBits = 156, ELF_ERR_InvalidNullSection = 157, ELF_ERR_InvalidAlignmentOutOfRange = 158, + ELF_ERR_MissingSectionHeaderIndices = 159, + ELF_ERR_MissingNullSection = 159, AR_ERR_InvalidMagicLength = 1000, AR_ERR_MagicNotFound = 1001, diff --git a/src/LibObjectFile/Elf/ElfObjectFile.cs b/src/LibObjectFile/Elf/ElfObjectFile.cs index a457826..a21f0ba 100644 --- a/src/LibObjectFile/Elf/ElfObjectFile.cs +++ b/src/LibObjectFile/Elf/ElfObjectFile.cs @@ -179,6 +179,12 @@ public override void Verify(DiagnosticBag diagnostics) diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderFileClassNone, $"Cannot compute the layout with an {nameof(ElfObjectFile)} having a {nameof(FileClass)} == {ElfFileClass.None}"); } + if (VisibleSectionCount >= ElfNative.SHN_LORESERVE && + Sections[0] is not ElfNullSection) + { + diagnostics.Error(DiagnosticId.ELF_ERR_MissingNullSection, $"Section count is higher than SHN_LORESERVE ({ElfNative.SHN_LORESERVE}) but the first section is not a NULL section"); + } + foreach (var segment in Segments) { segment.Verify(diagnostics); diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index 35fb0ff..cb9877f 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -385,7 +385,7 @@ public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMess if (errorMessageFormat == null) throw new ArgumentNullException(nameof(errorMessageFormat)); // Connect section Link instance - if (!link.IsSpecial) + if (!link.IsEmpty) { if (link.SpecialIndex == _sectionStringTableIndex) { @@ -396,13 +396,21 @@ public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMess var sectionIndex = link.SpecialIndex; bool sectionFound = false; - foreach (var section in ObjectFile.Sections) + if (sectionIndex < ObjectFile.Sections.Count && ObjectFile.Sections[(int)sectionIndex].SectionIndex == sectionIndex) { - if (section.SectionIndex == sectionIndex) + link = new ElfSectionLink(ObjectFile.Sections[(int)sectionIndex]); + sectionFound = true; + } + else + { + foreach (var section in ObjectFile.Sections) { - link = new ElfSectionLink(section); - sectionFound = true; - break; + if (section.SectionIndex == sectionIndex) + { + link = new ElfSectionLink(section); + sectionFound = true; + break; + } } } @@ -723,6 +731,9 @@ private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionTyp case ElfSectionType.Note: section = new ElfNoteTable(); break; + case ElfSectionType.SymbolTableSectionHeaderIndices: + section = new ElfSymbolTableSectionHeaderIndices(); + break; } // If the section is not a builtin section, try to offload to a delegate diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs index 4e7ab84..cd1c8f6 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTable.cs @@ -149,7 +149,8 @@ private void Write32(ElfWriter writer) writer.Encode(out sym.st_size, (uint)entry.Size); sym.st_info = (byte)(((byte) entry.Bind << 4) | (byte) entry.Type); sym.st_other = (byte) ((byte) entry.Visibility & 3); - writer.Encode(out sym.st_shndx, (ElfNative.Elf32_Half) entry.Section.GetIndex()); + var sectionIndex = entry.Section.GetIndex(); + writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf32_Half)sectionIndex : (ElfNative.Elf32_Half)ElfNative.SHN_XINDEX); writer.Write(sym); } @@ -169,7 +170,8 @@ private void Write64(ElfWriter writer) writer.Encode(out sym.st_size, entry.Size); sym.st_info = (byte)(((byte)entry.Bind << 4) | (byte)entry.Type); sym.st_other = (byte)((byte)entry.Visibility & 3); - writer.Encode(out sym.st_shndx, (ElfNative.Elf64_Half)entry.Section.GetIndex()); + var sectionIndex = entry.Section.GetIndex(); + writer.Encode(out sym.st_shndx, sectionIndex < ElfNative.SHN_LORESERVE || entry.Section.IsSpecial ? (ElfNative.Elf64_Half)sectionIndex : (ElfNative.Elf64_Half)ElfNative.SHN_XINDEX); writer.Write(sym); } @@ -183,6 +185,7 @@ protected override void AfterRead(ElfReader reader) for (int i = 0; i < Entries.Count; i++) { var entry = Entries[i]; + if (stringTable != null) { if (stringTable.TryResolve(entry.Name, out var newEntry)) @@ -195,7 +198,10 @@ protected override void AfterRead(ElfReader reader) } } - entry.Section = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.Section.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); + if (entry.Section.SpecialIndex < ElfNative.SHN_LORESERVE) + { + entry.Section = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.Section.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); + } Entries[i] = entry; } @@ -212,6 +218,7 @@ public override void Verify(DiagnosticBag diagnostics) } bool isAllowingLocal = true; + bool needsSectionHeaderIndices = false; for (int i = 0; i < Entries.Count; i++) { @@ -222,9 +229,14 @@ public override void Verify(DiagnosticBag diagnostics) diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSymbolEntryNonNull, $"Invalid entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The first entry must be null/undefined"); } - if (entry.Section.Section != null && entry.Section.Section.Parent != Parent) + if (entry.Section.Section != null) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The section of the entry `{entry}` must the same than this symbol table section"); + if (entry.Section.Section.Parent != Parent) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]. The section of the entry `{entry}` must the same than this symbol table section"); + } + + needsSectionHeaderIndices |= entry.Section.GetIndex() >= ElfNative.SHN_LORESERVE; } stringTable.ReserveString(entry.Name); @@ -244,6 +256,24 @@ public override void Verify(DiagnosticBag diagnostics) isAllowingLocal = false; } } + + if (needsSectionHeaderIndices) + { + bool foundSectionHeaderIndices = false; + foreach (ElfSection otherSection in Parent.Sections) + { + if (otherSection is ElfSymbolTableSectionHeaderIndices && otherSection.Link.Section == this) + { + foundSectionHeaderIndices = true; + break; + } + } + + if (!foundSectionHeaderIndices) + { + diagnostics.Error(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, $"Symbol table [{Name.Value}] references section indexes higher than SHN_LORESERVE and accompanying {nameof(ElfSymbolTableSectionHeaderIndices)} section is missing"); + } + } } public override unsafe void UpdateLayout(DiagnosticBag diagnostics) diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs new file mode 100644 index 0000000..3550c81 --- /dev/null +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -0,0 +1,112 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// This file is licensed under the BSD-Clause 2 license. +// See the license.txt file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace LibObjectFile.Elf +{ + /// + /// A section with the type + /// + public sealed class ElfSymbolTableSectionHeaderIndices : ElfSection + { + public const string DefaultName = ".symtab_shndx"; + + private readonly List _entries; + + public ElfSymbolTableSectionHeaderIndices() : base(ElfSectionType.SymbolTableSectionHeaderIndices) + { + Name = DefaultName; + _entries = new List(); + } + + public override ElfSectionType Type + { + get => base.Type; + set + { + if (value != ElfSectionType.SymbolTableSectionHeaderIndices) + { + throw new ArgumentException($"Invalid type `{Type}` of the section [{Index}] `{nameof(ElfSymbolTableSectionHeaderIndices)}`. Only `{ElfSectionType.SymbolTableSectionHeaderIndices}` is valid"); + } + base.Type = value; + } + } + + /// + /// Gets a list of entries. + /// + public IReadOnlyList Entries => _entries; + + public override unsafe ulong TableEntrySize => sizeof(uint); + + protected override void Read(ElfReader reader) + { + var numberOfEntries = base.Size / TableEntrySize; + _entries.Clear(); + _entries.Capacity = (int)numberOfEntries; + for (ulong i = 0; i < numberOfEntries; i++) + { + _entries.Add(new ElfSectionLink(reader.ReadU32())); + } + } + + protected override void Write(ElfWriter writer) + { + // Write all entries + for (int i = 0; i < Entries.Count; i++) + { + writer.WriteU32(Entries[i].GetIndex()); + } + } + + protected override void AfterRead(ElfReader reader) + { + // Verify that the link is safe and configured as expected + Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, reader.Diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); + + for (int i = 0; i < _entries.Count; i++) + { + var entry = _entries[i]; + if (entry.SpecialIndex != 0) + { + entry = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); + _entries[i] = entry; + + // Update the link in symbol table + var symbolTableEntry = symbolTable.Entries[i]; + symbolTableEntry.Section = entry.Section; + symbolTable.Entries[i] = symbolTableEntry; + } + } + } + + public override void Verify(DiagnosticBag diagnostics) + { + base.Verify(diagnostics); + + // Verify that the link is safe and configured as expected + if (!Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable)) + { + return; + } + + for (int i = 0; i < _entries.Count; i++) + { + var entry = _entries[i]; + if (entry.SpecialIndex != 0 && entry.Section != null && entry.Section.Parent != Parent) + { + diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]"); + } + } + } + + public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + { + if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : (ulong)Entries.Count * sizeof(uint); + } + } +} \ No newline at end of file From 7886efcd3a81e87f5811cd6ce615632a5b192007 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 11 Oct 2023 23:47:11 +0200 Subject: [PATCH 4/6] Remove dead code --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 9877561..7bb60ef 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -448,11 +448,6 @@ public void TestManySections() diagnostics = elf.Verify(); Assert.False(diagnostics.HasErrors); - for (int i = 0; i < ushort.MaxValue; i++) - { - - } - uint visibleSectionCount = elf.VisibleSectionCount; using (var outStream = File.OpenWrite("manysections")) From f51d461c29923ba32d7533d15c7994b472fb27f0 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 12 Oct 2023 00:28:58 +0200 Subject: [PATCH 5/6] Update the entries in ElfSymbolTableSectionHeaderIndices.UpdateLayout; check it in unit test --- src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs | 7 +++ .../ElfSymbolTableSectionHeaderIndices.cs | 61 ++++++++++++------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 7bb60ef..75e36f5 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -470,6 +470,13 @@ public void TestManySections() Assert.True(elf.Sections[i + 2] is ElfBinarySection); Assert.AreEqual($".section{i}", elf.Sections[i + 2].Name.Value); } + + Assert.True(elf.Sections[ushort.MaxValue + 3] is ElfSymbolTable); + symbolTable = (ElfSymbolTable)elf.Sections[ushort.MaxValue + 3]; + for (int i = 0; i < ushort.MaxValue; i++) + { + Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section.Name.Value); + } } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 3550c81..92fc3ff 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Reflection.PortableExecutable; namespace LibObjectFile.Elf { @@ -14,12 +15,12 @@ public sealed class ElfSymbolTableSectionHeaderIndices : ElfSection { public const string DefaultName = ".symtab_shndx"; - private readonly List _entries; + private readonly List _entries; public ElfSymbolTableSectionHeaderIndices() : base(ElfSectionType.SymbolTableSectionHeaderIndices) { Name = DefaultName; - _entries = new List(); + _entries = new List(); } public override ElfSectionType Type @@ -35,11 +36,6 @@ public override ElfSectionType Type } } - /// - /// Gets a list of entries. - /// - public IReadOnlyList Entries => _entries; - public override unsafe ulong TableEntrySize => sizeof(uint); protected override void Read(ElfReader reader) @@ -49,16 +45,16 @@ protected override void Read(ElfReader reader) _entries.Capacity = (int)numberOfEntries; for (ulong i = 0; i < numberOfEntries; i++) { - _entries.Add(new ElfSectionLink(reader.ReadU32())); + _entries.Add(reader.ReadU32()); } } protected override void Write(ElfWriter writer) { // Write all entries - for (int i = 0; i < Entries.Count; i++) + for (int i = 0; i < _entries.Count; i++) { - writer.WriteU32(Entries[i].GetIndex()); + writer.WriteU32(_entries[i]); } } @@ -70,14 +66,13 @@ protected override void AfterRead(ElfReader reader) for (int i = 0; i < _entries.Count; i++) { var entry = _entries[i]; - if (entry.SpecialIndex != 0) + if (entry != 0) { - entry = reader.ResolveLink(entry.Section, $"Invalid link section index {entry.SpecialIndex} for symbol table entry [{i}] from symbol table section [{this}]"); - _entries[i] = entry; + var resolvedLink = reader.ResolveLink(new ElfSectionLink(entry), $"Invalid link section index {entry} for symbol table entry [{i}] from symbol table section [{this}]"); // Update the link in symbol table var symbolTableEntry = symbolTable.Entries[i]; - symbolTableEntry.Section = entry.Section; + symbolTableEntry.Section = resolvedLink; symbolTable.Entries[i] = symbolTableEntry; } } @@ -92,21 +87,41 @@ public override void Verify(DiagnosticBag diagnostics) { return; } + } - for (int i = 0; i < _entries.Count; i++) + public override unsafe void UpdateLayout(DiagnosticBag diagnostics) + { + if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); + + // Verify that the link is safe and configured as expected + Link.TryGetSectionSafe(nameof(ElfSymbolTableSectionHeaderIndices), nameof(Link), this, diagnostics, out var symbolTable, ElfSectionType.SymbolTable, ElfSectionType.DynamicLinkerSymbolTable); + + int numberOfEntries = 0; + for (int i = 0; i < symbolTable.Entries.Count; i++) { - var entry = _entries[i]; - if (entry.SpecialIndex != 0 && entry.Section != null && entry.Section.Parent != Parent) + if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) { - diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSymbolEntrySectionParent, $"Invalid section for the symbol entry #{i} in the {nameof(ElfSymbolTable)} section [{Index}]"); + numberOfEntries = i + 1; } } - } - public override unsafe void UpdateLayout(DiagnosticBag diagnostics) - { - if (diagnostics == null) throw new ArgumentNullException(nameof(diagnostics)); - Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : (ulong)Entries.Count * sizeof(uint); + _entries.Capacity = numberOfEntries; + _entries.Clear(); + + for (int i = 0; i < numberOfEntries; i++) + { + var section = symbolTable.Entries[i].Section.Section; + if (section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + { + _entries.Add(section.SectionIndex); + } + else + { + _entries.Add(0); + } + } + + Size = Parent == null || Parent.FileClass == ElfFileClass.None ? 0 : (ulong)numberOfEntries * sizeof(uint); } } } \ No newline at end of file From 1ebfd4e9188e4a81844ce5d58bbb0a673f9795c9 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 12 Oct 2023 08:35:21 +0200 Subject: [PATCH 6/6] Fix incorrect and O(n^2) check for overlapping sections (triggered in the new ManySections test). --- src/LibObjectFile/Elf/ElfReader{TDecoder}.cs | 19 ++++++++++++------- .../ElfSymbolTableSectionHeaderIndices.cs | 1 - 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs index cb9877f..2ddcd83 100644 --- a/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs +++ b/src/LibObjectFile/Elf/ElfReader{TDecoder}.cs @@ -512,7 +512,7 @@ private void VerifyAndFixProgramHeadersAndSections() // Make sure to pre-sort all sections by offset var orderedSections = new List(ObjectFile.Sections.Count); orderedSections.AddRange(ObjectFile.Sections); - orderedSections.Sort(CompareSectionOffsetsDelegate); + orderedSections.Sort(CompareSectionOffsetsAndSizesDelegate); // Store the stream index to recover the same order when saving back. for(int i = 0; i < orderedSections.Count; i++) { @@ -548,10 +548,10 @@ private void VerifyAndFixProgramHeadersAndSections() lastOffset = section.Offset + section.Size - 1; // Verify overlapping sections and generate and error - for (int j = i + 1; j < orderedSections.Count; j++) + if (i + 1 < orderedSections.Count) { - var otherSection = orderedSections[j]; - if (section.Contains(otherSection) || otherSection.Contains(section)) + var otherSection = orderedSections[i + 1]; + if (otherSection.Offset < section.Offset + section.Size) { Diagnostics.Warning(DiagnosticId.ELF_ERR_InvalidOverlappingSections, $"The section {section} [{section.Offset} : {section.Offset + section.Size - 1}] is overlapping with the section {otherSection} [{otherSection.Offset} : {otherSection.Offset + otherSection.Size - 1}]"); } @@ -844,11 +844,16 @@ public override ushort Decode(ElfNative.Elf64_Versym src) return _decoder.Decode(src); } - private static readonly Comparison CompareSectionOffsetsDelegate = new Comparison(CompareSectionOffsets); + private static readonly Comparison CompareSectionOffsetsAndSizesDelegate = new Comparison(CompareSectionOffsetsAndSizes); - private static int CompareSectionOffsets(ElfSection left, ElfSection right) + private static int CompareSectionOffsetsAndSizes(ElfSection left, ElfSection right) { - return left.Offset.CompareTo(right.Offset); + int result = left.Offset.CompareTo(right.Offset); + if (result == 0) + { + result = left.Size.CompareTo(right.Size); + } + return result; } } } \ No newline at end of file diff --git a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs index 92fc3ff..483c529 100644 --- a/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Reflection.PortableExecutable; namespace LibObjectFile.Elf {