From ae9ea9b7f614df87c7924d69692ef17c8619b5fd Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 17 Oct 2023 21:57:15 -0700 Subject: [PATCH] Support opening the INI1 from a v17.0.0+ kernel --- src/LibHac/Kernel/IniExtract.cs | 93 ++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/src/LibHac/Kernel/IniExtract.cs b/src/LibHac/Kernel/IniExtract.cs index 5f6fc59a..e4bbc2f7 100644 --- a/src/LibHac/Kernel/IniExtract.cs +++ b/src/LibHac/Kernel/IniExtract.cs @@ -26,42 +26,73 @@ public static class IniExtract uint kernelSize = (uint)kernelSizeLong; - using (var array = new RentedArray(0x1000 + Unsafe.SizeOf())) - { - // The kernel map should be in the first 0x1000 bytes - if (kernelStorage.Read(0, array.Span).IsFailure()) - return false; - - ref byte start = ref array.Span[0]; - - // Search every 4 bytes for a valid kernel map - for (int i = 0; i < 0x1000; i += sizeof(int)) - { - ref KernelMap map = ref Unsafe.As(ref Unsafe.Add(ref start, i)); - - if (IsValidKernelMap(in map, kernelSize)) - { - // Verify the ini header at the offset in the found map - var header = new InitialProcessBinaryReader.IniHeader(); - - if (kernelStorage.Read(map.Ini1StartOffset, SpanHelpers.AsByteSpan(ref header)).IsFailure()) - return false; - - if (header.Magic != InitialProcessBinaryReader.ExpectedMagic) - return false; - - offset = (int)map.Ini1StartOffset; - size = header.Size; - return true; - } - } - + // .crt0 is located at the start of the kernel pre-17.0.0 + // 17.0.0+ kernels start with a "b crt0" branch instruction followed by 0x7FC of zeros + // The kernel map in this case will contain offsets relative to itself rather than to the start of the kernel + int crt0Offset = 0; + bool isMapAddressRelativeToItself = false; + + // Check if the first 4 bytes of the kernel is a branch instruction, and get the target if it is + ulong inst = 0; + if (kernelStorage.Read(0, SpanHelpers.AsByteSpan(ref inst)).IsFailure()) return false; + + if ((inst & 0xFFFFFFFFFF000000) == 0x0000000014000000) + { + crt0Offset = (int)((inst & 0x00FFFFFF) << 2); + isMapAddressRelativeToItself = true; } + + using var array = new RentedArray(0x1000 + Unsafe.SizeOf()); + if (kernelStorage.Read(crt0Offset, array.Span).IsFailure()) + return false; + + ref byte start = ref MemoryMarshal.GetReference(array.Span); + + // Search every 4 bytes for a valid kernel map + for (int i = 0; i < 0x1000 - Unsafe.SizeOf(); i += sizeof(int)) + { + ref KernelMap map = ref Unsafe.As(ref Unsafe.Add(ref start, i)); + uint mapOffsetAdjustment = isMapAddressRelativeToItself ? (uint)(crt0Offset + i) : 0; + + if (IsValidKernelMap(in map, kernelSize, mapOffsetAdjustment)) + { + // Verify the ini header at the offset in the found map + var header = new InitialProcessBinaryReader.IniHeader(); + + if (kernelStorage.Read(map.Ini1StartOffset + mapOffsetAdjustment, SpanHelpers.AsByteSpan(ref header)).IsFailure()) + return false; + + if (header.Magic != InitialProcessBinaryReader.ExpectedMagic) + continue; + + offset = (int)(map.Ini1StartOffset + mapOffsetAdjustment); + size = header.Size; + return true; + } + } + + return false; } - private static bool IsValidKernelMap(in KernelMap map, uint maxSize) + private static bool IsValidKernelMap(in KernelMap rawMap, uint maxSize, uint adj) { + KernelMap adjustedMap = rawMap; + adjustedMap.TextStartOffset += adj; + adjustedMap.TextEndOffset += adj; + adjustedMap.RodataStartOffset += adj; + adjustedMap.RodataEndOffset += adj; + adjustedMap.DataStartOffset += adj; + adjustedMap.DataEndOffset += adj; + adjustedMap.BssStartOffset += adj; + adjustedMap.BssEndOffset += adj; + adjustedMap.Ini1StartOffset += adj; + adjustedMap.DynamicOffset += adj; + adjustedMap.InitArrayStartOffset += adj; + adjustedMap.InitArrayStartOffset += adj; + + ref KernelMap map = ref adjustedMap; + if (map.TextStartOffset != 0) return false; if (map.TextStartOffset >= map.TextEndOffset) return false; if ((map.TextEndOffset & 0xFFF) != 0) return false;