Skip to content

Commit 79966c2

Browse files
authored
Merge pull request #31 from filipnavara/elf_many_sections
Add support for more than SHN_LORESERVE sections
2 parents e7a51a6 + 1ebfd4e commit 79966c2

8 files changed

+373
-36
lines changed

src/LibObjectFile.Tests/Elf/ElfSimpleTests.cs

+57
Original file line numberDiff line numberDiff line change
@@ -421,5 +421,62 @@ public void TestAlignedSection()
421421

422422
Assert.AreEqual(alignedSection.UpperAlignment, codeSection.Offset, "Invalid alignment");
423423
}
424+
425+
[Test]
426+
public void TestManySections()
427+
{
428+
var elf = new ElfObjectFile(ElfArch.X86_64);
429+
var stringTable = new ElfStringTable();
430+
var symbolTable = new ElfSymbolTable { Link = stringTable };
431+
432+
for (int i = 0; i < ushort.MaxValue; i++)
433+
{
434+
var section = new ElfBinarySection { Name = $".section{i}" };
435+
elf.AddSection(section);
436+
symbolTable.Entries.Add(new ElfSymbol { Type = ElfSymbolType.Section, Section = section });
437+
}
438+
439+
elf.AddSection(stringTable);
440+
elf.AddSection(symbolTable);
441+
elf.AddSection(new ElfSectionHeaderStringTable());
442+
443+
var diagnostics = elf.Verify();
444+
Assert.True(diagnostics.HasErrors);
445+
Assert.AreEqual(DiagnosticId.ELF_ERR_MissingSectionHeaderIndices, diagnostics.Messages[0].Id);
446+
447+
elf.AddSection(new ElfSymbolTableSectionHeaderIndices { Link = symbolTable });
448+
diagnostics = elf.Verify();
449+
Assert.False(diagnostics.HasErrors);
450+
451+
uint visibleSectionCount = elf.VisibleSectionCount;
452+
453+
using (var outStream = File.OpenWrite("manysections"))
454+
{
455+
elf.Write(outStream);
456+
outStream.Flush();
457+
}
458+
459+
using (var inStream = File.OpenRead("manysections"))
460+
{
461+
elf = ElfObjectFile.Read(inStream);
462+
}
463+
464+
Assert.AreEqual(visibleSectionCount, elf.VisibleSectionCount);
465+
Assert.True(elf.Sections[0] is ElfNullSection);
466+
Assert.True(elf.Sections[1] is ElfProgramHeaderTable);
467+
468+
for (int i = 0; i < ushort.MaxValue; i++)
469+
{
470+
Assert.True(elf.Sections[i + 2] is ElfBinarySection);
471+
Assert.AreEqual($".section{i}", elf.Sections[i + 2].Name.Value);
472+
}
473+
474+
Assert.True(elf.Sections[ushort.MaxValue + 3] is ElfSymbolTable);
475+
symbolTable = (ElfSymbolTable)elf.Sections[ushort.MaxValue + 3];
476+
for (int i = 0; i < ushort.MaxValue; i++)
477+
{
478+
Assert.AreEqual($".section{i}", symbolTable.Entries[i + 1].Section.Section.Name.Value);
479+
}
480+
}
424481
}
425482
}

src/LibObjectFile/DiagnosticId.cs

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public enum DiagnosticId
6969
ELF_ERR_InvalidStreamForSectionNoBits = 156,
7070
ELF_ERR_InvalidNullSection = 157,
7171
ELF_ERR_InvalidAlignmentOutOfRange = 158,
72+
ELF_ERR_MissingSectionHeaderIndices = 159,
73+
ELF_ERR_MissingNullSection = 159,
7274

7375
AR_ERR_InvalidMagicLength = 1000,
7476
AR_ERR_MagicNotFound = 1001,

src/LibObjectFile/Elf/ElfObjectFile.cs

+6
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ public override void Verify(DiagnosticBag diagnostics)
179179
diagnostics.Error(DiagnosticId.ELF_ERR_InvalidHeaderFileClassNone, $"Cannot compute the layout with an {nameof(ElfObjectFile)} having a {nameof(FileClass)} == {ElfFileClass.None}");
180180
}
181181

182+
if (VisibleSectionCount >= ElfNative.SHN_LORESERVE &&
183+
Sections[0] is not ElfNullSection)
184+
{
185+
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");
186+
}
187+
182188
foreach (var segment in Segments)
183189
{
184190
segment.Verify(diagnostics);

src/LibObjectFile/Elf/ElfReader{TDecoder}.cs

+117-22
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.IO;
8+
using static System.Collections.Specialized.BitVector32;
89

910
namespace LibObjectFile.Elf
1011
{
@@ -17,8 +18,8 @@ internal abstract class ElfReader<TDecoder> : ElfReader where TDecoder : struct,
1718
private TDecoder _decoder;
1819
private ulong _startOfFile;
1920
private ushort _programHeaderCount;
20-
private ushort _sectionHeaderCount;
21-
private ushort _sectionStringTableIndex;
21+
private uint _sectionHeaderCount;
22+
private uint _sectionStringTableIndex;
2223
private bool _isFirstSectionValidNull;
2324
private bool _hasValidSectionStringTable;
2425

@@ -193,7 +194,7 @@ private ElfSegment ReadProgramHeader64(int phdrIndex)
193194

194195
private void ReadSections()
195196
{
196-
if (_sectionHeaderCount == 0) return;
197+
if (Layout.OffsetOfSectionHeaderTable == 0) return;
197198

198199
// Write section header table
199200
ReadSectionHeaderTable();
@@ -210,9 +211,25 @@ private void ReadSectionHeaderTable()
210211
return;
211212
}
212213

213-
for (int i = 0; i < _sectionHeaderCount; i++)
214+
uint i = 0;
215+
216+
if (_sectionHeaderCount == 0)
217+
{
218+
// We are dealing with an object file that has more than SHN_LORESERVE
219+
// (0xff00) sections. It has to begin with a NULL section header where
220+
// its Size contains the real number of sections, and Link optionally
221+
// points to string table section if it's section index is too high.
222+
if (ReadExtendedNullSectionTableEntry())
223+
{
224+
i = 1;
225+
ObjectFile.AddSection(new ElfNullSection());
226+
_isFirstSectionValidNull = true;
227+
}
228+
}
229+
230+
for (; i < _sectionHeaderCount; i++)
214231
{
215-
var offset = Layout.OffsetOfSectionHeaderTable + (ulong)i * Layout.SizeOfSectionHeaderEntry;
232+
var offset = Layout.OffsetOfSectionHeaderTable + i * Layout.SizeOfSectionHeaderEntry;
216233

217234
if (offset >= (ulong)Stream.Length)
218235
{
@@ -228,12 +245,12 @@ private void ReadSectionHeaderTable()
228245
}
229246
}
230247

231-
private ElfSection ReadSectionTableEntry(int sectionIndex)
248+
private ElfSection ReadSectionTableEntry(uint sectionIndex)
232249
{
233250
return ObjectFile.FileClass == ElfFileClass.Is32 ? ReadSectionTableEntry32(sectionIndex) : ReadSectionTableEntry64(sectionIndex);
234251
}
235252

236-
private ElfSection ReadSectionTableEntry32(int sectionIndex)
253+
private ElfSection ReadSectionTableEntry32(uint sectionIndex)
237254
{
238255
var streamOffset = Stream.Position;
239256
if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection))
@@ -267,7 +284,7 @@ private ElfSection ReadSectionTableEntry32(int sectionIndex)
267284
return section;
268285
}
269286

270-
private ElfSection ReadSectionTableEntry64(int sectionIndex)
287+
private ElfSection ReadSectionTableEntry64(uint sectionIndex)
271288
{
272289
var streamOffset = Stream.Position;
273290
if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection))
@@ -300,13 +317,75 @@ private ElfSection ReadSectionTableEntry64(int sectionIndex)
300317

301318
return section;
302319
}
320+
321+
private bool ReadExtendedNullSectionTableEntry()
322+
{
323+
uint sh_type;
324+
ulong sh_size;
325+
uint sh_link;
326+
bool isNull;
327+
328+
Stream.Position = (long)Layout.OffsetOfSectionHeaderTable;
329+
330+
if (ObjectFile.FileClass == ElfFileClass.Is32)
331+
{
332+
333+
if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf32_Shdr rawSection32))
334+
{
335+
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");
336+
return false;
337+
}
338+
339+
sh_type = _decoder.Decode(rawSection32.sh_type);
340+
sh_size = _decoder.Decode(rawSection32.sh_size);
341+
sh_link = _decoder.Decode(rawSection32.sh_link);
342+
rawSection32.sh_size = 0;
343+
rawSection32.sh_link = 0;
344+
isNull = rawSection32.IsNull;
345+
}
346+
else
347+
{
348+
if (!TryReadData(Layout.SizeOfSectionHeaderEntry, out ElfNative.Elf64_Shdr rawSection64))
349+
{
350+
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");
351+
return false;
352+
}
353+
354+
sh_type = _decoder.Decode(rawSection64.sh_type);
355+
sh_size = _decoder.Decode(rawSection64.sh_size);
356+
sh_link = _decoder.Decode(rawSection64.sh_link);
357+
rawSection64.sh_size = 0;
358+
rawSection64.sh_link = 0;
359+
isNull = rawSection64.IsNull;
360+
}
361+
362+
if (!isNull)
363+
{
364+
Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidFirstSectionExpectingUndefined, $"Invalid Section [0] {(ElfSectionType)sh_type}. Expecting {ElfNative.SHN_UNDEF}");
365+
return false;
366+
}
367+
368+
if (sh_size >= uint.MaxValue)
369+
{
370+
Diagnostics.Error(DiagnosticId.ELF_ERR_InvalidSectionHeaderCount, $"Extended section count [{sh_size}] exceeds {uint.MaxValue}");
371+
return false;
372+
}
373+
374+
_sectionHeaderCount = (uint)sh_size;
375+
if (_sectionStringTableIndex == ElfNative.SHN_XINDEX)
376+
{
377+
_sectionStringTableIndex = sh_link;
378+
}
379+
380+
return true;
381+
}
303382

304383
public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMessageFormat)
305384
{
306385
if (errorMessageFormat == null) throw new ArgumentNullException(nameof(errorMessageFormat));
307386

308387
// Connect section Link instance
309-
if (!link.IsSpecial)
388+
if (!link.IsEmpty)
310389
{
311390
if (link.SpecialIndex == _sectionStringTableIndex)
312391
{
@@ -317,13 +396,21 @@ public override ElfSectionLink ResolveLink(ElfSectionLink link, string errorMess
317396
var sectionIndex = link.SpecialIndex;
318397

319398
bool sectionFound = false;
320-
foreach (var section in ObjectFile.Sections)
399+
if (sectionIndex < ObjectFile.Sections.Count && ObjectFile.Sections[(int)sectionIndex].SectionIndex == sectionIndex)
321400
{
322-
if (section.SectionIndex == sectionIndex)
401+
link = new ElfSectionLink(ObjectFile.Sections[(int)sectionIndex]);
402+
sectionFound = true;
403+
}
404+
else
405+
{
406+
foreach (var section in ObjectFile.Sections)
323407
{
324-
link = new ElfSectionLink(section);
325-
sectionFound = true;
326-
break;
408+
if (section.SectionIndex == sectionIndex)
409+
{
410+
link = new ElfSectionLink(section);
411+
sectionFound = true;
412+
break;
413+
}
327414
}
328415
}
329416

@@ -425,7 +512,7 @@ private void VerifyAndFixProgramHeadersAndSections()
425512
// Make sure to pre-sort all sections by offset
426513
var orderedSections = new List<ElfSection>(ObjectFile.Sections.Count);
427514
orderedSections.AddRange(ObjectFile.Sections);
428-
orderedSections.Sort(CompareSectionOffsetsDelegate);
515+
orderedSections.Sort(CompareSectionOffsetsAndSizesDelegate);
429516
// Store the stream index to recover the same order when saving back.
430517
for(int i = 0; i < orderedSections.Count; i++)
431518
{
@@ -461,10 +548,10 @@ private void VerifyAndFixProgramHeadersAndSections()
461548
lastOffset = section.Offset + section.Size - 1;
462549

463550
// Verify overlapping sections and generate and error
464-
for (int j = i + 1; j < orderedSections.Count; j++)
551+
if (i + 1 < orderedSections.Count)
465552
{
466-
var otherSection = orderedSections[j];
467-
if (section.Contains(otherSection) || otherSection.Contains(section))
553+
var otherSection = orderedSections[i + 1];
554+
if (otherSection.Offset < section.Offset + section.Size)
468555
{
469556
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}]");
470557
}
@@ -609,7 +696,7 @@ private void VerifyAndFixProgramHeadersAndSections()
609696
}
610697
}
611698

612-
private ElfSection CreateElfSection(int sectionIndex, ElfSectionType sectionType, bool isNullSection)
699+
private ElfSection CreateElfSection(uint sectionIndex, ElfSectionType sectionType, bool isNullSection)
613700
{
614701
ElfSection section = null;
615702

@@ -644,6 +731,9 @@ private ElfSection CreateElfSection(int sectionIndex, ElfSectionType sectionType
644731
case ElfSectionType.Note:
645732
section = new ElfNoteTable();
646733
break;
734+
case ElfSectionType.SymbolTableSectionHeaderIndices:
735+
section = new ElfSymbolTableSectionHeaderIndices();
736+
break;
647737
}
648738

649739
// 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)
754844
return _decoder.Decode(src);
755845
}
756846

757-
private static readonly Comparison<ElfSection> CompareSectionOffsetsDelegate = new Comparison<ElfSection>(CompareSectionOffsets);
847+
private static readonly Comparison<ElfSection> CompareSectionOffsetsAndSizesDelegate = new Comparison<ElfSection>(CompareSectionOffsetsAndSizes);
758848

759-
private static int CompareSectionOffsets(ElfSection left, ElfSection right)
849+
private static int CompareSectionOffsetsAndSizes(ElfSection left, ElfSection right)
760850
{
761-
return left.Offset.CompareTo(right.Offset);
851+
int result = left.Offset.CompareTo(right.Offset);
852+
if (result == 0)
853+
{
854+
result = left.Size.CompareTo(right.Size);
855+
}
856+
return result;
762857
}
763858
}
764859
}

0 commit comments

Comments
 (0)