diff --git a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs index 658975d..75e36f5 100644 --- a/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs +++ b/src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs @@ -421,5 +421,62 @@ public void TestAlignedSection() Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Offset, "Invalid alignment"); } + + [Test] + 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++) + { + 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); + + 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); + 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); + } + + 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/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 6f5edec..2ddcd83 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) + { + // 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 + (ulong)i * Layout.SizeOfSectionHeaderEntry; + 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,13 +317,75 @@ 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) { if (errorMessageFormat == null) throw new ArgumentNullException(nameof(errorMessageFormat)); // Connect section Link instance - if (!link.IsSpecial) + if (!link.IsEmpty) { if (link.SpecialIndex == _sectionStringTableIndex) { @@ -317,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; + } } } @@ -425,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++) { @@ -461,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}]"); } @@ -609,7 +696,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; @@ -644,6 +731,9 @@ private ElfSection CreateElfSection(int sectionIndex, ElfSectionType sectionType 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 @@ -754,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/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. /// 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..483c529 --- /dev/null +++ b/src/LibObjectFile/Elf/Sections/ElfSymbolTableSectionHeaderIndices.cs @@ -0,0 +1,126 @@ +// 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; + } + } + + 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(reader.ReadU32()); + } + } + + protected override void Write(ElfWriter writer) + { + // Write all entries + for (int i = 0; i < _entries.Count; i++) + { + writer.WriteU32(_entries[i]); + } + } + + 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 != 0) + { + 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 = resolvedLink; + 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; + } + } + + 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++) + { + if (symbolTable.Entries[i].Section.Section is { SectionIndex: >= ElfNative.SHN_LORESERVE }) + { + numberOfEntries = i + 1; + } + } + + _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