Skip to content

Commit

Permalink
Merge pull request #31 from filipnavara/elf_many_sections
Browse files Browse the repository at this point in the history
Add support for more than SHN_LORESERVE sections
  • Loading branch information
xoofx authored Oct 14, 2023
2 parents e7a51a6 + 1ebfd4e commit 79966c2
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 36 deletions.
57 changes: 57 additions & 0 deletions src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
2 changes: 2 additions & 0 deletions src/LibObjectFile/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/LibObjectFile/Elf/ElfObjectFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
139 changes: 117 additions & 22 deletions src/LibObjectFile/Elf/ElfReader{TDecoder}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using static System.Collections.Specialized.BitVector32;

namespace LibObjectFile.Elf
{
Expand All @@ -17,8 +18,8 @@ internal abstract class ElfReader<TDecoder> : 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;

Expand Down Expand Up @@ -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();
Expand All @@ -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)
{
Expand All @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
}
}
}

Expand Down Expand Up @@ -425,7 +512,7 @@ private void VerifyAndFixProgramHeadersAndSections()
// Make sure to pre-sort all sections by offset
var orderedSections = new List<ElfSection>(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++)
{
Expand Down Expand Up @@ -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}]");
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -754,11 +844,16 @@ public override ushort Decode(ElfNative.Elf64_Versym src)
return _decoder.Decode(src);
}

private static readonly Comparison<ElfSection> CompareSectionOffsetsDelegate = new Comparison<ElfSection>(CompareSectionOffsets);
private static readonly Comparison<ElfSection> CompareSectionOffsetsAndSizesDelegate = new Comparison<ElfSection>(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;
}
}
}
Loading

0 comments on commit 79966c2

Please sign in to comment.