4internal static class NdsElfParser
7 private const int HeaderSize = 52;
9 private const int ProgramHeaderSize = 32;
15 public static NdsElfFile Parse(ReadOnlyMemory<byte> data, NdsElfImportOptions options)
17 ReadOnlySpan<byte> bytes = data.Span;
18 if (bytes.Length < HeaderSize)
20 throw new InvalidDataException(
"The ELF file is shorter than its 52-byte ELF32 header.");
23 if (bytes[0] != 0x7F || !bytes.Slice(1, 3).SequenceEqual(
"ELF"u8))
25 throw new InvalidDataException(
"The input does not begin with the ELF magic number.");
28 if (bytes[4] != 1 || bytes[5] != 1 || bytes[6] != 1)
30 throw new InvalidDataException(
"NDS ingestion requires ELF32, little-endian encoding, and identification version one.");
33 if (NdsBinary.ReadUInt16(bytes, 16) != 2 || NdsBinary.ReadUInt16(bytes, 18) != 40 ||
34 NdsBinary.ReadUInt32(bytes, 20) != 1 || NdsBinary.ReadUInt16(bytes, 40) != HeaderSize)
36 throw new InvalidDataException(
"NDS ingestion requires an executable ARM ELF with a canonical ELF32 header.");
39 uint tableOffset = NdsBinary.ReadUInt32(bytes, 28);
40 ushort entrySize = NdsBinary.ReadUInt16(bytes, 42);
41 ushort count = NdsBinary.ReadUInt16(bytes, 44);
42 if (count == 0 || count > options.MaxProgramHeaders || entrySize != ProgramHeaderSize)
44 throw new InvalidDataException(
"The ELF program-header count or entry width is unsupported.");
47 long tableLength = checked((
long)count * ProgramHeaderSize);
48 if (tableOffset > bytes.Length || tableLength > bytes.Length - tableOffset)
50 throw new InvalidDataException(
"The ELF program-header table lies outside the input.");
53 var segments =
new NdsElfSegment[count];
54 for (
int index = 0; index < count; index++)
56 int offset = checked((
int)tableOffset + (index * ProgramHeaderSize));
57 ReadOnlySpan<byte> item = bytes.Slice(offset, ProgramHeaderSize);
58 var segment =
new NdsElfSegment(
59 NdsBinary.ReadUInt32(item, 0),
60 NdsBinary.ReadUInt32(item, 4),
61 NdsBinary.ReadUInt32(item, 8),
62 NdsBinary.ReadUInt32(item, 12),
63 NdsBinary.ReadUInt32(item, 16),
64 NdsBinary.ReadUInt32(item, 20),
65 NdsBinary.ReadUInt32(item, 24),
66 NdsBinary.ReadUInt32(item, 28));
67 ValidateSegment(segment, bytes.Length, index);
68 segments[index] = segment;
71 return new(NdsBinary.ReadUInt32(bytes, 24), segments);
78 private static void ValidateSegment(NdsElfSegment segment,
int fileLength,
int index)
80 if (segment.IsLoadable && segment.MemorySize < segment.FileSize)
82 throw new InvalidDataException($
"ELF program header {index} has a memory size smaller than its file image.");
85 if (segment.FileOffset > fileLength || segment.FileSize > fileLength - segment.FileOffset)
87 throw new InvalidDataException($
"ELF program header {index} references bytes outside the input.");
90 if (segment.Alignment != 0 && (segment.Alignment & (segment.Alignment - 1)) != 0)
92 throw new InvalidDataException($
"ELF program header {index} has a non-power-of-two alignment.");
95 if (segment.IsLoadable && segment.Alignment > 1 &&
96 segment.VirtualAddress % segment.Alignment != segment.FileOffset % segment.Alignment)
98 throw new InvalidDataException($
"ELF loadable program header {index} has incongruent file and virtual alignment.");
101 if (segment.IsLoadable &&
102 ((ulong)segment.PhysicalAddress + segment.MemorySize > 0x1_0000_0000UL ||
103 (ulong)segment.VirtualAddress + segment.MemorySize > 0x1_0000_0000UL))
105 throw new InvalidDataException($
"ELF loadable program header {index} wraps the 32-bit address space.");