4internal static class NdsElfAssembler
12 public static NdsElfImportResult Assemble(
13 ReadOnlyMemory<byte> data,
16 NdsElfImportOptions options)
18 bool twl = processor is NdsProcessor.Arm9i or
NdsProcessor.Arm7i;
19 NdsElfSegment[] selectedSegments = elf.Segments
20 .Where(segment => segment.IsLoadable && !segment.IsOverlay && segment.IsTwl == twl)
22 NdsElfSegment[] mainSegments = selectedSegments
23 .Where(
static segment => segment.FileSize != 0)
24 .OrderBy(
static segment => segment.PhysicalAddress)
26 if (mainSegments.Length == 0)
28 throw new InvalidDataException($
"The ELF contains no file-backed segments for {processor}.");
31 uint start = mainSegments[0].PhysicalAddress;
32 ulong end = mainSegments.Max(
static segment => (ulong)segment.PhysicalAddress + segment.FileSize);
33 ulong outputLength = end - start;
34 if (outputLength > (uint)options.MaxProgramBytes)
36 throw new InvalidDataException(
"The ELF's contiguous Program extent exceeds the configured output limit.");
39 ValidateNoFileOverlap(mainSegments);
40 var contents =
new byte[(int)outputLength];
41 foreach (NdsElfSegment segment
in mainSegments)
43 int sourceOffset = checked((
int)segment.FileOffset);
44 int destinationOffset = checked((
int)(segment.PhysicalAddress - start));
45 data.Span.Slice(sourceOffset, checked((
int)segment.FileSize)).CopyTo(contents.AsSpan(destinationOffset));
48 uint entryPoint = TranslateEntryPoint(elf.EntryPoint, mainSegments);
49 var program =
new NdsProgramDefinition(processor, contents, start, entryPoint);
50 NdsElfSegment[] overlaySegments = elf.Segments
51 .Where(
static segment => segment.IsLoadable && segment.IsOverlay)
53 NdsOverlayDefinition[] overlays = options.ImportOverlays &&
54 overlaySegments.Length != 0 &&
55 processor is NdsProcessor.Arm9 or NdsProcessor.Arm7
56 ? ImportOverlays(data, overlaySegments, processor, options)
58 uint? arm7WramAddress = processor == NdsProcessor.Arm7i
59 ? FindArm7WramAddress(selectedSegments)
61 return new(program, overlays, overlaySegments.Length != 0, arm7WramAddress);
66 private static void ValidateNoFileOverlap(NdsElfSegment[] segments)
68 ulong previousEnd = 0;
69 for (
int index = 0; index < segments.Length; index++)
71 NdsElfSegment segment = segments[index];
72 if (index != 0 && segment.PhysicalAddress < previousEnd)
74 throw new InvalidDataException(
"ELF file-backed Program segments overlap in physical memory.");
77 previousEnd = (ulong)segment.PhysicalAddress + segment.FileSize;
85 private static uint TranslateEntryPoint(uint entryPoint, IEnumerable<NdsElfSegment> segments)
87 foreach (NdsElfSegment segment
in segments)
89 ulong virtualEnd = (ulong)segment.VirtualAddress + segment.FileSize;
90 if (entryPoint >= segment.VirtualAddress && entryPoint < virtualEnd)
92 return checked(segment.PhysicalAddress + (entryPoint - segment.VirtualAddress));
102 private static uint? FindArm7WramAddress(IEnumerable<NdsElfSegment> segments) => segments
103 .Select(
static segment => segment.VirtualAddress)
105 .FirstOrDefault(
static address => address is >= 0x0300_0000 and < 0x037F_8000);
113 private static NdsOverlayDefinition[] ImportOverlays(
114 ReadOnlyMemory<byte> data,
115 NdsElfSegment[] segments,
117 NdsElfImportOptions options)
119 NdsElfSegment table = segments[0];
120 if (table.FileSize == 0 || table.FileSize % 12 != 0)
122 throw new InvalidDataException(
"The ELF Overlay metadata segment must contain complete 12-byte records.");
125 int count = checked((
int)(table.FileSize / 12));
126 if (count > options.MaxOverlays || segments.Length != count + 1)
128 throw new InvalidDataException(
"The ELF Overlay record and payload segment counts are inconsistent or exceed policy.");
131 var result =
new NdsOverlayDefinition[count];
132 ReadOnlySpan<byte> tableData = data.Span.Slice(checked((
int)table.FileOffset), checked((
int)table.FileSize));
133 for (
int index = 0; index < count; index++)
135 NdsElfSegment payload = segments[index + 1];
136 ReadOnlySpan<byte> record = tableData.Slice(index * 12, 12);
137 uint control = NdsBinary.ReadUInt32(record, 8);
138 byte[] contents = data.Span
139 .Slice(checked((
int)payload.FileOffset), checked((
int)payload.FileSize))
143 checked((uint)index),
145 payload.VirtualAddress,
147 payload.MemorySize - payload.FileSize,
148 NdsBinary.ReadUInt32(record, 0),
149 NdsBinary.ReadUInt32(record, 4),
150 control & 0x00FF_FFFF,
151 (byte)(control >> 24));
NdsProcessor
Identifies a processor and execution mode.