mirror of
https://github.com/LukeFZ/Il2CppInspectorRedux.git
synced 2026-03-22 00:18:18 +05:00
add initial ported nso loader
This commit is contained in:
257
Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs
Normal file
257
Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using VersionedSerialization;
|
||||
using VersionedSerialization.Attributes;
|
||||
|
||||
namespace Il2CppInspector;
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct NsoHeader
|
||||
{
|
||||
public const uint ExpectedMagic = 0x304F534E; // NSO0 (LE)
|
||||
|
||||
public uint Magic;
|
||||
public uint Version;
|
||||
public uint Reserved;
|
||||
public SegmentFlags Flags;
|
||||
|
||||
public SegmentHeader TextSegment;
|
||||
public uint ModuleNameOffset;
|
||||
|
||||
public SegmentHeader RoSegment;
|
||||
public uint ModuleNameSize;
|
||||
|
||||
public SegmentHeader DataSegment;
|
||||
public uint BssSize;
|
||||
|
||||
public ModuleIdBuffer ModuleId;
|
||||
|
||||
public uint TextFileSize;
|
||||
public uint RoFileSize;
|
||||
public uint DataFileSize;
|
||||
|
||||
public ReservedBuffer Reserved2;
|
||||
|
||||
public SegmentHeaderRelative ApiInfoSection;
|
||||
public SegmentHeaderRelative DynStrSection;
|
||||
public SegmentHeaderRelative DynSymInfo;
|
||||
|
||||
public HashBuffer TextHash;
|
||||
public HashBuffer RoHash;
|
||||
public HashBuffer DataHash;
|
||||
|
||||
[InlineArray(32)]
|
||||
public struct ModuleIdBuffer
|
||||
{
|
||||
private byte _value0;
|
||||
}
|
||||
|
||||
[InlineArray(0x1c)]
|
||||
public struct ReservedBuffer
|
||||
{
|
||||
private byte _value0;
|
||||
}
|
||||
|
||||
[InlineArray(32)]
|
||||
public struct HashBuffer
|
||||
{
|
||||
private byte _value0;
|
||||
}
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct SegmentHeaderRelative
|
||||
{
|
||||
public uint Offset;
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct SegmentHeader
|
||||
{
|
||||
public uint FileOffset;
|
||||
public uint MemoryOffset;
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SegmentFlags : uint
|
||||
{
|
||||
TextCompress = 1 << 0,
|
||||
RoCompress = 1 << 1,
|
||||
DataCompress = 1 << 2,
|
||||
TextHash = 1 << 3,
|
||||
RoHash = 1 << 4,
|
||||
DataHash = 1 << 5
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct ModInfoHeader
|
||||
{
|
||||
public uint Reserved;
|
||||
public uint ModOffset;
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct ModHeader
|
||||
{
|
||||
public const uint ExpectedMagic = 0x30444f4d; // MOD0
|
||||
|
||||
public readonly bool ValidMagic => Magic == ExpectedMagic;
|
||||
|
||||
public uint Magic;
|
||||
public uint DynamicOffset;
|
||||
public uint BssStartOffset;
|
||||
public uint BssEndOffset;
|
||||
public uint EhFrameHdrStartOffset;
|
||||
public uint EhFrameHdrEndOffset;
|
||||
public uint ModuleOffset;
|
||||
}
|
||||
|
||||
// These are just regular ELF stucts, but as long as the ELF parser still
|
||||
// uses the old format we'll duplicate them here
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct DynamicEntry
|
||||
{
|
||||
[NativeInteger]
|
||||
public DynamicTag Tag;
|
||||
|
||||
[NativeInteger]
|
||||
public ulong Value;
|
||||
}
|
||||
|
||||
public enum DynamicTag : long
|
||||
{
|
||||
DT_NULL = 0,
|
||||
DT_NEEDED = 1,
|
||||
DT_PLTRELSZ = 2,
|
||||
DT_PLTGOT = 0x3,
|
||||
DT_HASH = 0x4,
|
||||
DT_STRTAB = 0x5,
|
||||
DT_SYMTAB = 0x6,
|
||||
DT_RELA = 0x7,
|
||||
DT_RELASZ = 0x8,
|
||||
DT_RELAENT = 0x9,
|
||||
DT_STRSZ = 0xa,
|
||||
DT_SYMENT = 0xb,
|
||||
DT_INIT = 0xC,
|
||||
DT_FINI = 0xD,
|
||||
DT_REL = 0x11,
|
||||
DT_RELSZ = 0x12,
|
||||
DT_RELENT = 0x13,
|
||||
DT_PLTREL = 0x14,
|
||||
DT_DEBUG = 0x15,
|
||||
DT_TEXTREL = 0x16,
|
||||
DT_JMPREL = 0x17,
|
||||
DT_BIND_NOW = 0x18,
|
||||
DT_INIT_ARRAY = 0x19,
|
||||
DT_FINI_ARRAY = 0x1A,
|
||||
DT_INIT_ARRAYSZ = 0x1B,
|
||||
DT_FINI_ARRAYSZ = 0x1C,
|
||||
DT_RUNPATH = 0x1D,
|
||||
DT_FLAGS = 0x1E,
|
||||
DT_PREINIT_ARRAY = 0x20,
|
||||
DT_PREINIT_ARRAYSZ = 0x21,
|
||||
DT_LOOS = 0x6000000D,
|
||||
DT_ANDROID_REL = DT_LOOS + 2,
|
||||
DT_ANDROID_RELSZ = DT_LOOS + 3,
|
||||
DT_ANDROID_RELA = DT_LOOS + 4,
|
||||
DT_ANDROID_RELASZ = DT_LOOS + 5
|
||||
}
|
||||
|
||||
public struct SymbolEntry : IReadable
|
||||
{
|
||||
public const int Size32Bit = sizeof(uint) + sizeof(uint) + sizeof(byte) + sizeof(byte) + sizeof(ushort);
|
||||
public const int Size64Bit = sizeof(byte) + sizeof(byte) + sizeof(ushort) + sizeof(ulong) + sizeof(ulong);
|
||||
|
||||
public uint Name;
|
||||
public ulong Value;
|
||||
public ulong Size;
|
||||
public byte Info;
|
||||
public byte Other;
|
||||
public ushort SectionIndex;
|
||||
|
||||
public readonly ElfSymbolBind Bind => (ElfSymbolBind)(Info >> 4);
|
||||
public readonly ElfSymbolType Type => (ElfSymbolType)(Info & 0xf);
|
||||
|
||||
public void Read<TReader>(ref Reader<TReader> reader, in StructVersion version = default)
|
||||
where TReader : IReader, allows ref struct
|
||||
{
|
||||
reader.ReadPrimitive(ref Name);
|
||||
|
||||
if (reader.Config.Is32Bit)
|
||||
{
|
||||
Value = reader.ReadNativeUInt();
|
||||
Size = reader.ReadPrimitive<uint>();
|
||||
Info = reader.ReadPrimitive<byte>();
|
||||
Other = reader.ReadPrimitive<byte>();
|
||||
SectionIndex = reader.ReadPrimitive<ushort>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Info = reader.ReadPrimitive<byte>();
|
||||
Other = reader.ReadPrimitive<byte>();
|
||||
SectionIndex = reader.ReadPrimitive<ushort>();
|
||||
Value = reader.ReadNativeUInt();
|
||||
Size = reader.ReadNativeUInt();
|
||||
}
|
||||
}
|
||||
|
||||
static int IReadable.Size(in StructVersion version, in ReaderConfig config)
|
||||
=> config.Is32Bit ? Size32Bit : Size64Bit;
|
||||
}
|
||||
|
||||
public enum ElfSymbolBind
|
||||
{
|
||||
STB_LOCAL,
|
||||
STB_GLOBAL,
|
||||
STB_WEAK
|
||||
}
|
||||
|
||||
public enum ElfSymbolType
|
||||
{
|
||||
STT_NOTYPE,
|
||||
STT_OBJECT,
|
||||
STT_FUNC,
|
||||
STT_SECTION,
|
||||
STT_FILE,
|
||||
STT_COMMON,
|
||||
STT_TLS
|
||||
}
|
||||
|
||||
public enum RelocationType : uint
|
||||
{
|
||||
R_ARM_ABS32 = 2,
|
||||
R_ARM_REL32 = 3,
|
||||
R_ARM_PC13 = 4,
|
||||
R_ARM_COPY = 20,
|
||||
|
||||
R_AARCH64_ABS64 = 0x101,
|
||||
R_AARCH64_PREL64 = 0x104,
|
||||
R_AARCH64_GLOB_DAT = 0x401,
|
||||
R_AARCH64_JUMP_SLOT = 0x402,
|
||||
R_AARCH64_RELATIVE = 0x403,
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct RelEntry
|
||||
{
|
||||
[NativeInteger]
|
||||
public ulong Offset;
|
||||
|
||||
[NativeInteger]
|
||||
public ulong Info;
|
||||
}
|
||||
|
||||
[VersionedStruct]
|
||||
public partial struct RelaEntry
|
||||
{
|
||||
[NativeInteger]
|
||||
public ulong Offset;
|
||||
|
||||
[NativeInteger]
|
||||
public ulong Info;
|
||||
|
||||
[NativeInteger]
|
||||
public long Addend;
|
||||
}
|
||||
339
Il2CppInspector.Common/FileFormatStreams/NsoReader.cs
Normal file
339
Il2CppInspector.Common/FileFormatStreams/NsoReader.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
#nullable enable
|
||||
|
||||
using K4os.Compression.LZ4;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using VersionedSerialization;
|
||||
|
||||
namespace Il2CppInspector;
|
||||
|
||||
internal abstract class MemoryBackingBuffer : IDisposable
|
||||
{
|
||||
public abstract bool Initialized { get; protected set; }
|
||||
|
||||
public bool IsLittleEndian { get; protected set; }
|
||||
public bool Is32Bit { get; protected set; }
|
||||
public ulong BaseAddress { get; protected set; }
|
||||
|
||||
protected abstract Span<byte> Data { get; }
|
||||
|
||||
public virtual void Initialize(long capacity, bool littleEndian, bool is32Bit, ulong baseAddress)
|
||||
{
|
||||
IsLittleEndian = littleEndian;
|
||||
Is32Bit = is32Bit;
|
||||
BaseAddress = baseAddress;
|
||||
}
|
||||
|
||||
protected int TranslateVaToRva(ulong address)
|
||||
{
|
||||
Debug.Assert(address >= BaseAddress);
|
||||
var relativeAddress = address - BaseAddress;
|
||||
return checked((int)relativeAddress);
|
||||
}
|
||||
|
||||
public T ReadObject<T>(ulong address, in StructVersion version) where T : unmanaged, IReadable
|
||||
{
|
||||
Debug.Assert(Initialized);
|
||||
|
||||
return IsLittleEndian
|
||||
? Reader.LittleEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadVersionedObject<T>(in version)
|
||||
: Reader.BigEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadVersionedObject<T>(in version);
|
||||
}
|
||||
|
||||
public ImmutableArray<T> ReadObjectArray<T>(ulong address, long count, in StructVersion version) where T : unmanaged, IReadable
|
||||
{
|
||||
if (count == 0)
|
||||
return [];
|
||||
|
||||
Debug.Assert(Initialized);
|
||||
|
||||
return IsLittleEndian
|
||||
? Reader.LittleEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadVersionedObjectArray<T>(count, in version)
|
||||
: Reader.BigEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadVersionedObjectArray<T>(count, in version);
|
||||
}
|
||||
|
||||
public T ReadPrimitive<T>(ulong address) where T : unmanaged
|
||||
{
|
||||
Debug.Assert(Initialized);
|
||||
|
||||
return IsLittleEndian
|
||||
? Reader.LittleEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadPrimitive<T>()
|
||||
: Reader.BigEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit))
|
||||
.ReadPrimitive<T>();
|
||||
}
|
||||
|
||||
public void WriteNUInt(ulong address, ulong value)
|
||||
{
|
||||
var region = Data.Slice(TranslateVaToRva(address), Is32Bit ? sizeof(uint) : sizeof(ulong));
|
||||
if (Is32Bit)
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(region, (uint)value);
|
||||
else
|
||||
BinaryPrimitives.WriteUInt32BigEndian(region, (uint)value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsLittleEndian)
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(region, value);
|
||||
else
|
||||
BinaryPrimitives.WriteUInt64BigEndian(region, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteBytes(ulong address, ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
bytes.CopyTo(Data.Slice(TranslateVaToRva(address), bytes.Length));
|
||||
}
|
||||
|
||||
public void Clear(ulong address, long size)
|
||||
{
|
||||
Data.Slice(TranslateVaToRva(address), checked((int)size)).Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ByteArrayBackingBuffer : MemoryBackingBuffer
|
||||
{
|
||||
[MemberNotNullWhen(true, nameof(Buffer))]
|
||||
public override bool Initialized { get; protected set; }
|
||||
|
||||
public byte[]? Buffer { get; private set; }
|
||||
|
||||
protected override Span<byte> Data => Buffer.AsSpan();
|
||||
|
||||
public override void Initialize(long capacity, bool littleEndian, bool is32Bit, ulong baseAddress)
|
||||
{
|
||||
base.Initialize(capacity, littleEndian, is32Bit, baseAddress);
|
||||
Buffer = new byte[capacity];
|
||||
Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class NsoReader : FileFormatStream<NsoReader>
|
||||
{
|
||||
public override string DefaultFilename => "main";
|
||||
public override int Bits => _is32Bit ? 32 : 64;
|
||||
public override string Arch => "ARM64";
|
||||
public override string Format => "NSO";
|
||||
|
||||
// NOTE: This does not work for some reason?
|
||||
// public override ulong ImageBase => 0x7100000000;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_mappedExecutable.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override bool Init()
|
||||
{
|
||||
var reader = VersionedSerialization.Reader.LittleEndian(GetBuffer());
|
||||
var magic = reader.ReadPrimitive<uint>();
|
||||
if (magic != NsoHeader.ExpectedMagic)
|
||||
return false;
|
||||
|
||||
LoadInternal();
|
||||
|
||||
Position = 0;
|
||||
Write(_mappedExecutable.Buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly ByteArrayBackingBuffer _mappedExecutable = new();
|
||||
private bool _is32Bit;
|
||||
private FrozenDictionary<DynamicTag, ulong> _dynamicEntries = FrozenDictionary<DynamicTag, ulong>.Empty;
|
||||
private ImmutableArray<(ulong Start, ulong End)> _relocationEntryRegions = [];
|
||||
|
||||
private void LoadInternal()
|
||||
{
|
||||
var reader = VersionedSerialization.Reader.LittleEndian(GetBuffer());
|
||||
var header = reader.ReadVersionedObject<NsoHeader>();
|
||||
|
||||
var totalLength = header.TextSegment.Size + header.RoSegment.Size + header.DataSegment.Size + header.BssSize;
|
||||
|
||||
_mappedExecutable.Initialize(totalLength, true, false, 0);
|
||||
|
||||
LoadSegment(ref reader, in header.TextSegment, header.TextFileSize, header.Flags.HasFlag(SegmentFlags.TextCompress));
|
||||
LoadSegment(ref reader, in header.RoSegment, header.RoFileSize, header.Flags.HasFlag(SegmentFlags.RoCompress));
|
||||
LoadSegment(ref reader, in header.DataSegment, header.DataFileSize, header.Flags.HasFlag(SegmentFlags.DataCompress));
|
||||
|
||||
var modInfoHeader = _mappedExecutable.ReadObject<ModInfoHeader>(header.TextSegment.MemoryOffset, default);
|
||||
var modHeader = _mappedExecutable.ReadObject<ModHeader>(modInfoHeader.ModOffset, default);
|
||||
|
||||
Debug.Assert(modHeader.ValidMagic);
|
||||
|
||||
// check if we are loading a 32-bit binary
|
||||
// by checking if we have a full .dynamic entry in the first (or third) dynamic slot
|
||||
var firstEntry = _mappedExecutable.ReadPrimitive<ulong>(modInfoHeader.ModOffset + modHeader.DynamicOffset);
|
||||
var thirdEntry = _mappedExecutable.ReadPrimitive<ulong>(modInfoHeader.ModOffset + modHeader.DynamicOffset + (2 * 0x8));
|
||||
_is32Bit = firstEntry > uint.MaxValue || thirdEntry > uint.MaxValue;
|
||||
|
||||
var currentDynamicOffset = modInfoHeader.ModOffset + modHeader.DynamicOffset;
|
||||
var dynamicEntries = new Dictionary<DynamicTag, ulong>();
|
||||
while (true)
|
||||
{
|
||||
var entry = _mappedExecutable.ReadObject<DynamicEntry>(currentDynamicOffset, default);
|
||||
if (entry.Tag == DynamicTag.DT_NULL)
|
||||
break;
|
||||
|
||||
dynamicEntries[entry.Tag] = entry.Value;
|
||||
currentDynamicOffset += 2 * (Is32Bit ? 4u : 8u);
|
||||
}
|
||||
|
||||
_dynamicEntries = dynamicEntries.ToFrozenDictionary();
|
||||
|
||||
ApplyRelocations();
|
||||
}
|
||||
|
||||
private void LoadSegment<T>(ref Reader<T> reader, ref readonly SegmentHeader header, uint fileSize, bool isCompressed)
|
||||
where T : ISeekableReader, allows ref struct
|
||||
{
|
||||
reader.Offset = checked((int)header.FileOffset);
|
||||
var data = reader.ReadBytes(checked((int)fileSize));
|
||||
|
||||
if (!isCompressed)
|
||||
{
|
||||
Debug.Assert(header.Size == fileSize);
|
||||
_mappedExecutable.WriteBytes(header.MemoryOffset, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rented = ArrayPool<byte>.Shared.Rent(checked((int)header.Size));
|
||||
var decompressed = rented.AsSpan(0, checked((int)header.Size));
|
||||
|
||||
var result = LZ4Codec.Decode(data, decompressed);
|
||||
Debug.Assert(result == header.Size);
|
||||
_ = result;
|
||||
|
||||
_mappedExecutable.WriteBytes(header.MemoryOffset, decompressed);
|
||||
|
||||
ArrayPool<byte>.Shared.Return(rented);
|
||||
}
|
||||
}
|
||||
|
||||
// Copied and trimmed down from ElfBinary
|
||||
private void ApplyRelocations()
|
||||
{
|
||||
if (_mappedExecutable == null)
|
||||
throw new InvalidOperationException("Cannot apply relocations without executable being mapped");
|
||||
|
||||
if (!_dynamicEntries.TryGetValue(DynamicTag.DT_SYMTAB, out var symtabAddress)
|
||||
|| !_dynamicEntries.TryGetValue(DynamicTag.DT_SYMENT, out var symtabEntrySize))
|
||||
return;
|
||||
|
||||
List<(ulong Start, ulong End)> relocationRegions = [];
|
||||
|
||||
if (_dynamicEntries.TryGetValue(DynamicTag.DT_REL, out var relAddress))
|
||||
{
|
||||
var relocationSize = _dynamicEntries[DynamicTag.DT_RELSZ];
|
||||
ApplyRelocationsImpl(ParseRelSection(relAddress, relocationSize));
|
||||
relocationRegions.Add((relAddress, relAddress + relocationSize));
|
||||
}
|
||||
else if (_dynamicEntries.TryGetValue(DynamicTag.DT_RELA, out var relaAddress))
|
||||
{
|
||||
var relocationSize = _dynamicEntries[DynamicTag.DT_RELASZ];
|
||||
ApplyRelocationsImpl(ParseRelaSection(relaAddress, relocationSize));
|
||||
relocationRegions.Add((relaAddress, relaAddress + relocationSize));
|
||||
}
|
||||
|
||||
if (_dynamicEntries.TryGetValue(DynamicTag.DT_JMPREL, out var jmprelAddress))
|
||||
{
|
||||
var size = _dynamicEntries[DynamicTag.DT_PLTRELSZ];
|
||||
var type = (DynamicTag)_dynamicEntries[DynamicTag.DT_PLTREL];
|
||||
ApplyRelocationsImpl(type == DynamicTag.DT_REL
|
||||
? ParseRelSection(jmprelAddress, size)
|
||||
: ParseRelaSection(jmprelAddress, size));
|
||||
|
||||
relocationRegions.Add((jmprelAddress, jmprelAddress + size));
|
||||
}
|
||||
|
||||
_relocationEntryRegions = [.. relocationRegions];
|
||||
|
||||
// Clear out relocation sections in memory so searching is faster
|
||||
foreach (var (start, end) in _relocationEntryRegions)
|
||||
{
|
||||
_mappedExecutable.Clear(start, checked((long)(end - start)));
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void ApplyRelocationsImpl(ICollection<(ulong Offset, ulong Info, long? Addend)> relocations)
|
||||
{
|
||||
if (relocations.Count == 0)
|
||||
return;
|
||||
|
||||
var symbolCache = new Dictionary<uint, SymbolEntry>();
|
||||
|
||||
foreach (var relocation in relocations)
|
||||
{
|
||||
var offset = relocation.Offset;
|
||||
var type = (RelocationType)(Is32Bit ? relocation.Info & byte.MaxValue : relocation.Info & uint.MaxValue);
|
||||
var symbolIndex = (uint)(relocation.Info >> (Is32Bit ? 8 : 32));
|
||||
var addend = checked((ulong)(relocation.Addend ?? _mappedExecutable.ReadPrimitive<long>(offset)));
|
||||
|
||||
if (!symbolCache.TryGetValue(symbolIndex, out var symbolEntry))
|
||||
{
|
||||
var symtabEntryAddress = symtabAddress + symbolIndex * symtabEntrySize;
|
||||
symbolEntry = _mappedExecutable.ReadObject<SymbolEntry>(symtabEntryAddress, default);
|
||||
symbolCache[symbolIndex] = symbolEntry;
|
||||
}
|
||||
|
||||
var (value, handled) = type switch
|
||||
{
|
||||
RelocationType.R_ARM_ABS32 or RelocationType.R_AARCH64_ABS64 => (symbolEntry.Value + addend, true),
|
||||
RelocationType.R_ARM_REL32 or RelocationType.R_AARCH64_PREL64 => (symbolEntry.Value + relocation.Offset - addend, true),
|
||||
RelocationType.R_ARM_COPY => (symbolEntry.Value, true),
|
||||
RelocationType.R_AARCH64_GLOB_DAT => (symbolEntry.Value + addend, true),
|
||||
RelocationType.R_AARCH64_JUMP_SLOT => (symbolEntry.Value + addend, true),
|
||||
RelocationType.R_AARCH64_RELATIVE => (symbolEntry.Value + addend, true),
|
||||
_ => (0uL, false)
|
||||
};
|
||||
|
||||
if (handled)
|
||||
{
|
||||
_mappedExecutable.WriteNUInt(offset, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<(ulong, ulong, long?)> ParseRelSection(ulong address, ulong size)
|
||||
{
|
||||
var entrySize = _dynamicEntries[DynamicTag.DT_RELENT];
|
||||
var entryCount = size / entrySize;
|
||||
|
||||
return _mappedExecutable.ReadObjectArray<RelEntry>(address, checked((int)entryCount), default)
|
||||
.Select<RelEntry, (ulong, ulong, long?)>(x => (x.Offset, x.Info, null))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
List<(ulong, ulong, long?)> ParseRelaSection(ulong address, ulong size)
|
||||
{
|
||||
var entrySize = _dynamicEntries[DynamicTag.DT_RELAENT];
|
||||
var entryCount = size / entrySize;
|
||||
|
||||
return _mappedExecutable.ReadObjectArray<RelaEntry>(address, checked((int)entryCount), default)
|
||||
.Select<RelaEntry, (ulong, ulong, long?)>(x => (x.Offset, x.Info, x.Addend))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="dnlib" Version="4.4.0" />
|
||||
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="2.0.0" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.50.0" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user