Files

613 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using LibCpp2IL.Logging;
using LibCpp2IL.Metadata;
using LibCpp2IL.PE;
namespace LibCpp2IL.Elf;
public sealed class ElfFile : ElfStyleRelocationsBinary
{
private byte[] _raw;
private List<IElfProgramHeaderEntry> _elfProgramHeaderEntries;
private readonly List<ElfSectionHeaderEntry> _elfSectionHeaderEntries;
private ElfFileIdent? _elfFileIdent;
private ElfFileHeader? _elfHeader;
private readonly List<ElfDynamicEntry> _dynamicSection = [];
private readonly List<ElfSymbolTableEntry> _symbolTable = [];
private readonly Dictionary<string, ElfSymbolTableEntry> _exportNameTable = new();
private readonly Dictionary<ulong, ElfSymbolTableEntry> _exportAddressTable = new();
private List<long>? _initializerPointers;
private long _globalOffset;
public ElfFile(MemoryStream input) : base(input)
{
_raw = input.GetBuffer();
LibLogger.Verbose("\tReading Elf File Ident...");
var start = DateTime.Now;
ReadAndValidateIdent();
var isBigEndian = _elfFileIdent!.Endianness == 2;
LibLogger.VerboseNewline($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
LibLogger.VerboseNewline($"\tBinary is {(is32Bit ? "32-bit" : "64-bit")} and {(isBigEndian ? "big-endian" : "little-endian")}.");
if (isBigEndian)
SetBigEndian();
LibLogger.Verbose("\tReading and validating full ELF header...");
start = DateTime.Now;
ReadHeader();
LibLogger.VerboseNewline($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
LibLogger.VerboseNewline($"\tElf File contains instructions of type {InstructionSetId}");
LibLogger.Verbose("\tReading ELF program header table...");
start = DateTime.Now;
ReadProgramHeaderTable();
LibLogger.VerboseNewline($"Read {_elfProgramHeaderEntries!.Count} OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
LibLogger.VerboseNewline("\tReading ELF section header table and names...");
start = DateTime.Now;
//Non-null assertion reason: The elf header has already been checked while reading the program header.
try
{
_elfSectionHeaderEntries = ReadReadableArrayAtRawAddr<ElfSectionHeaderEntry>(_elfHeader!.pSectionHeader, _elfHeader.SectionHeaderEntryCount).ToList();
}
catch (Exception)
{
_elfSectionHeaderEntries = [];
}
if (_elfHeader!.SectionNameSectionOffset >= 0 && _elfHeader.SectionNameSectionOffset < _elfSectionHeaderEntries.Count)
{
var pSectionHeaderStringTable = _elfSectionHeaderEntries[_elfHeader.SectionNameSectionOffset].RawAddress;
foreach (var section in _elfSectionHeaderEntries)
{
section.Name = ReadStringToNull(pSectionHeaderStringTable + section.NameOffset);
LibLogger.VerboseNewline($"\t\t-Name for section at 0x{section.RawAddress:X} is {section.Name}");
}
}
LibLogger.VerboseNewline($"\tRead {_elfSectionHeaderEntries.Count} OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
if (_elfSectionHeaderEntries.FirstOrDefault(s => s.Name == ".text") is { } textSection)
{
_globalOffset = (long)textSection.VirtualAddress - (long)textSection.RawAddress;
}
else
{
var execSegment = _elfProgramHeaderEntries!.First(p => (p.Flags & ElfProgramHeaderFlags.PF_X) != 0);
_globalOffset = (long)execSegment.VirtualAddress - (long)execSegment.RawAddress;
}
LibLogger.VerboseNewline($"\tELF global offset is 0x{_globalOffset:X}");
//Get dynamic section.
if (GetProgramHeaderOfType(ElfProgramEntryType.PT_DYNAMIC) is { } dynamicSegment)
{
// NOTE: Do not use .RawAddress here, as it is not guaranteed to point to the actual dynamic table being used.
// The dynamic table should be mapped by one of the preceding PT_LOAD entries.
// Source: phdr_table_get_dynamic_section, https://cs.android.com/android/platform/superproject/main/+/main:bionic/linker/linker_phdr.cpp
_dynamicSection = ReadReadableArrayAtVirtualAddress<ElfDynamicEntry>(dynamicSegment.VirtualAddress, (int)dynamicSegment.RawSize / (is32Bit ? 8 : 16))
.TakeWhile(x => x.Tag != ElfDynamicType.DT_NULL)
.ToList();
}
LibLogger.VerboseNewline("\tFinding Relocations...");
start = DateTime.Now;
ProcessRelocations();
LibLogger.VerboseNewline($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
LibLogger.VerboseNewline("\tProcessing Symbols...");
start = DateTime.Now;
try
{
ProcessSymbols();
LibLogger.VerboseNewline($"\tOK ({(DateTime.Now - start).TotalMilliseconds} ms)");
}
catch (Exception e)
{
LibLogger.ErrorNewline($"\tCaught {e.GetType().Name} processing symbols! Attempting to continue without symbol information (no exports, for example)...");
#if DEBUG
LibLogger.ErrorNewline(e.ToString());
#endif
}
LibLogger.Verbose("\tProcessing Initializers...");
start = DateTime.Now;
ProcessInitializers();
LibLogger.VerboseNewline($"Got {_initializerPointers!.Count} OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
}
private void ReadAndValidateIdent()
{
_elfFileIdent = ReadReadable<ElfFileIdent>(0);
if (_elfFileIdent.Magic != 0x464c457f) //Magic number
throw new FormatException("ERROR: Magic number mismatch.");
if (_elfFileIdent.Architecture == 1)
is32Bit = true;
else if (_elfFileIdent.Architecture != 2)
throw new FormatException($"Invalid arch number (expecting 1 or 2): {_elfFileIdent.Architecture}");
if (_elfFileIdent.Version != 1)
throw new FormatException($"ELF Version is not 1? File header has version {_elfFileIdent.Version}");
}
private void ReadHeader()
{
_elfHeader = ReadReadable<ElfFileHeader>(0x10);
InstructionSetId = _elfHeader.Machine switch
{
0x03 => DefaultInstructionSets.X86_32,
0x3E => DefaultInstructionSets.X86_64,
0x28 => DefaultInstructionSets.ARM_V7,
0xB7 => DefaultInstructionSets.ARM_V8,
_ => throw new NotImplementedException($"ELF Machine {_elfHeader.Machine} not implemented")
};
if (_elfHeader.Version != 1)
throw new FormatException($"Full ELF header specifies version {_elfHeader.Version}, only supported version is 1.");
}
private void ReadProgramHeaderTable()
{
_elfProgramHeaderEntries = is32Bit
? ReadReadableArrayAtRawAddr<ElfProgramHeaderEntry32>(_elfHeader!.pProgramHeader, _elfHeader.ProgramHeaderEntryCount).Cast<IElfProgramHeaderEntry>().ToList()
: ReadReadableArrayAtRawAddr<ElfProgramHeaderEntry64>(_elfHeader!.pProgramHeader, _elfHeader.ProgramHeaderEntryCount).Cast<IElfProgramHeaderEntry>().ToList();
}
private IElfProgramHeaderEntry? GetProgramHeaderOfType(ElfProgramEntryType type) => _elfProgramHeaderEntries.FirstOrDefault(p => p.Type == type);
private IEnumerable<ElfSectionHeaderEntry> GetSections(ElfSectionEntryType type) => _elfSectionHeaderEntries.Where(s => s.Type == type);
private ElfSectionHeaderEntry? GetSingleSection(ElfSectionEntryType type) => GetSections(type).FirstOrDefault();
private ElfDynamicEntry? GetDynamicEntryOfType(ElfDynamicType type) => _dynamicSection.FirstOrDefault(d => d.Tag == type);
private void ProcessRelocations()
{
try
{
ApplyRelocations(_dynamicSection
.Where(x => x.Tag != ElfDynamicType.DT_NEEDED)
.ToDictionary(x => x.Tag, x => x.Value),
(ulong)0,
(ElfMachine)_elfHeader!.Machine);
}
catch
{
LibLogger.Info("Exception during relocation mapping!");
throw;
}
}
private void ProcessSymbols()
{
var symbolTables = new List<(ulong offset, ulong count, ulong strings)>();
//Look for .strtab
if (GetSingleSection(ElfSectionEntryType.SHT_STRTAB) is { } strTab)
{
//Look for .symtab
if (GetSingleSection(ElfSectionEntryType.SHT_SYMTAB) is { } symtab)
{
LibLogger.VerboseNewline($"\t\t-Found .symtab at 0x{symtab.RawAddress:X}");
symbolTables.Add((symtab.RawAddress, symtab.Size / (ulong)symtab.EntrySize, strTab.RawAddress));
}
//Look for .dynsym
if (GetSingleSection(ElfSectionEntryType.SHT_DYNSYM) is { } dynsym)
{
LibLogger.VerboseNewline($"\t\t-Found .dynsym at 0x{dynsym.RawAddress:X}");
symbolTables.Add((dynsym.RawAddress, dynsym.Size / (ulong)dynsym.EntrySize, strTab.RawAddress));
}
}
//Look for Dynamic String table
if (GetDynamicEntryOfType(ElfDynamicType.DT_STRTAB) is { } dynamicStrTab)
{
if (GetDynamicEntryOfType(ElfDynamicType.DT_SYMTAB) is { } dynamicSymTab)
{
var endSection = _dynamicSection.Where(x => x.Value > dynamicSymTab.Value).OrderBy(x => x.Value).FirstOrDefault();
ulong end;
if(endSection != null)
end = endSection.Value;
else
end = GetProgramHeaderOfType(ElfProgramEntryType.PT_DYNAMIC) is {} dynamicSegment ? dynamicSegment.VirtualAddress + dynamicSegment.RawSize : (ulong)RawLength;
var dynSymSize = is32Bit ? 18ul : 24ul;
var address = (ulong)MapVirtualAddressToRaw(dynamicSymTab.Value);
LibLogger.VerboseNewline($"\t\t-Found DT_SYMTAB at 0x{address:X}");
symbolTables.Add((
address,
(end - dynamicSymTab.Value) / dynSymSize,
dynamicStrTab.Value
));
}
}
_symbolTable.Clear();
_exportNameTable.Clear();
_exportAddressTable.Clear();
//Unify symbol tables
foreach (var (offset, count, stringTable) in symbolTables)
{
var symbols = is32Bit
? ReadReadableArrayAtRawAddr<ElfDynamicSymbol32>((long)offset, (long)count).Cast<IElfDynamicSymbol>().ToList()
: ReadReadableArrayAtRawAddr<ElfDynamicSymbol64>((long)offset, (long)count).Cast<IElfDynamicSymbol>().ToList();
LibLogger.VerboseNewline($"\t\t-Found {symbols.Count} symbols in table at 0x{offset:X}");
foreach (var symbol in symbols)
{
string name;
try
{
name = ReadStringToNull(stringTable + symbol.NameOffset);
}
catch (ArgumentOutOfRangeException)
{
// Stripped
continue;
}
var usefulType = symbol.Shndx == 0 ? ElfSymbolTableEntry.ElfSymbolEntryType.Import
: symbol.Type == ElfDynamicSymbolType.STT_FUNC ? ElfSymbolTableEntry.ElfSymbolEntryType.Function
: symbol.Type == ElfDynamicSymbolType.STT_OBJECT || symbol.Type == ElfDynamicSymbolType.STT_COMMON ? ElfSymbolTableEntry.ElfSymbolEntryType.Name
: ElfSymbolTableEntry.ElfSymbolEntryType.Unknown;
var virtualAddress = symbol.Value;
var entry = new ElfSymbolTableEntry { Name = name, Type = usefulType, VirtualAddress = virtualAddress };
_symbolTable.Add(entry);
if (symbol.Shndx != 0)
{
_exportNameTable.TryAdd(name, entry);
_exportAddressTable.TryAdd(virtualAddress, entry);
}
}
}
}
private void ProcessInitializers()
{
if (!(GetDynamicEntryOfType(ElfDynamicType.DT_INIT_ARRAY) is { } dtInitArray) || !(GetDynamicEntryOfType(ElfDynamicType.DT_INIT_ARRAYSZ) is { } dtInitArraySz))
{
_initializerPointers = [];
return;
}
var pInitArray = MapVirtualAddressToRaw(dtInitArray.Value);
var count = (int)dtInitArraySz.Value / (is32Bit ? 4 : 8);
var initArray = ReadNUintArrayAtRawAddress(pInitArray, count);
if (GetDynamicEntryOfType(ElfDynamicType.DT_INIT) is { } dtInit)
initArray = initArray.Append(dtInit.Value).ToArray();
_initializerPointers = initArray.Select(a => MapVirtualAddressToRaw(a)).ToList();
}
public override (ulong pCodeRegistration, ulong pMetadataRegistration) FindCodeAndMetadataReg(Il2CppMetadata metadata)
{
//Let's just try and be cheap here and find them in the symbol table.
LibLogger.Verbose("\tChecking ELF Symbol Table for code and/or meta reg...");
ulong codeReg = 0;
ulong metadataReg = 0;
if (_symbolTable.FirstOrDefault(s => s.Name.Contains("g_CodeRegistration")) is { } codeRegSymbol)
codeReg = codeRegSymbol.VirtualAddress;
if (_symbolTable.FirstOrDefault(s => s.Name.Contains("g_MetadataRegistration")) is { } metaRegSymbol)
metadataReg = metaRegSymbol.VirtualAddress;
if (codeReg != 0 && metadataReg != 0)
{
LibLogger.VerboseNewline("Found them.");
return (codeReg, metadataReg);
}
LibLogger.VerboseNewline("Didn't find them, scanning binary...");
//Well, that didn't work. Look for the specific initializer function which calls into Il2CppCodegenRegistration.
if (InstructionSetId == DefaultInstructionSets.ARM_V7 && metadata.MetadataVersion < 24.2f)
{
var ret = FindCodeAndMetadataRegArm32();
if (ret != (0, 0))
return ret;
}
if (InstructionSetId == DefaultInstructionSets.ARM_V8 && metadata.MetadataVersion < 24.2f)
{
var ret = FindCodeAndMetadataRegArm64();
if (ret != (0, 0))
return ret;
}
return FindCodeAndMetadataRegDefaultBehavior(metadata);
}
private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegArm32()
{
//This is a little complicated, so:
//All ARM instructions are four bytes.
//We need to check for two out of a specific 6 instructions, so 24 (0x18) bytes.
//And we need to do this for all initializer functions.
//Specifically, we're looking for:
//ADD r0, pc, r0 (00 00 8f e0)
//ADD r1, pc, r1 (01 10 8f e0)
var addSearchBytes = new byte[] { 0x00, 0x00, 0x8F, 0xE0, 0x01, 0x10, 0x8F, 0xE0 };
//Also, the third instruction should be LDR R1, #x. But we don't know what x is, but it contains the pointer to the CodegenRegistration function.
//So search for the bytes that *don't* specify what x is. There are three.
var ldrSearchBytes = new byte[] { 0x10, 0x9F, 0xE5 };
LibLogger.VerboseNewline($"\tARM-32 MODE: Checking {_initializerPointers!.Count} initializer pointers...");
foreach (var initializerPointer in _initializerPointers!) //Not-null asserted because it's initialized in the constructor.
{
//So, read 0x18 bytes.
var instructionBytes = ReadByteArrayAtRawAddress(initializerPointer, 0x18);
//We only want the last two instructions, so skip the first 16 bytes.
if (!addSearchBytes.SequenceEqual(instructionBytes.Skip(0x10)))
continue;
//Check last three bytes of third instruction (so skip 9 bytes, read 3)
if (!ldrSearchBytes.SequenceEqual(instructionBytes.Skip(9).Take(3)))
continue;
//Take the 8th byte, which contains our 'x' value, which specifies where the codereg function is.
//Add the current PC value. ARM is weird in that the PC points to two instructions *after* the currently executing one.
//This instruction is offset 0x8, each instruction is 4 bytes, so two instructions below is 0x10 into the function.
//So add 0x10, to the function address, to the value in byte 8.
var pointerToPointerToCodegenRegFunction = instructionBytes[8] + initializerPointer + 0x10;
//Now we know where the function pointer is. Or, the important part of it.
//Read 4 bytes there.
Position = pointerToPointerToCodegenRegFunction;
var pointerToCodegenRegFunction = ReadUInt32();
//Pointer is relative, so add on address of function + offset of pointer table (?) in function (0x1C).
pointerToCodegenRegFunction += (uint)initializerPointer + 0x1C;
//Read 7 instructions + 3 pointers which should hopefully make up Il2CppCodegenRegistration.
//functionBody[0] through [6] are instructions, [7] through [9] are pointers.
var functionBody = ReadClassArrayAtRawAddr<uint>(pointerToCodegenRegFunction, 10);
//Check the last instruction is an unconditional branch
if (functionBody[6].Bits(24, 8) != 0b_1110_1010)
continue;
//Read the three register-value pairs for the first 3 LDRs in the function.
var registerOffsets = new uint[3];
var fail = false;
for (var i = 0u; i <= 2u && !fail; i++)
{
var (registerNum, immediate) = ArmUtils.GetOperandsForLiteralLdr(functionBody[i]);
if (registerNum > 2 || immediate == 0) //Immediate = 0 is a fail, register > 2 indicates wrong function.
fail = true;
else
registerOffsets[registerNum] = immediate + i * 4 + 8; //PC is +8, i*4 is 4 bytes per instruction.
}
if (fail)
continue;
//Instructions 3-5 (4, 5, 6) load the actual data values. They can be LDR or ADD, where:
//LDR indicates we have a pointer-to-pointer and have to read the struct pointer from elsewhere in the binary.
//ADD indicates we have a relative pointer to the data and just resolve that to the struct pointer.
var pointers = new uint[3];
for (var i = 3u; i <= 5 && !fail; i++)
{
var (addFirstReg, addSecondReg, addThirdReg) = ArmUtils.GetOperandsForRegisterAdd(functionBody[i]);
var (ldrFirstReg, ldrSecondReg, ldrThirdReg) = ArmUtils.GetOperandsForRegisterLdr(functionBody[i]);
if (addSecondReg == ArmUtils.PC_REG && addFirstReg == addThirdReg && addFirstReg <= 2)
//Valid ADD
pointers[addFirstReg] = pointerToCodegenRegFunction + i * 4 + functionBody[registerOffsets[addFirstReg] / 4] + 8;
else if (ldrSecondReg == ArmUtils.PC_REG && ldrFirstReg == ldrThirdReg && ldrFirstReg <= 2)
{
//Valid LDR.
var p = pointerToCodegenRegFunction + i * 4 + functionBody[registerOffsets[ldrFirstReg] / 4] + 8;
//VIRTUAL address
//We're a 32-bit binary if we're here, so we can just read the pointer as a 32-bit value.
pointers[ldrFirstReg] = (uint)ReadPointerAtVirtualAddress(p);
}
else
fail = true;
}
if (fail)
{
LibLogger.VerboseNewline($"\t\tInitializer function at 0x{initializerPointer:X} is probably NOT the il2cpp initializer.");
continue;
}
LibLogger.VerboseNewline($"\t\tFound valid sequence of bytes for il2cpp initializer function at 0x{initializerPointer:X}.");
return (pointers[0], pointers[1]);
}
return (0, 0);
}
private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegArm64()
{
LibLogger.VerboseNewline($"\tARM-64 MODE: Checking {_initializerPointers!.Count} initializer pointers...");
foreach (var initializerPointer in _initializerPointers)
{
//In most cases we don't need more than the first 7 instructions
var func = MiniArm64Decompiler.ReadFunctionAtRawAddress(this, (uint)initializerPointer, 7);
//Don't accept anything longer than 7 instructions
//I.e. if it doesn't end with a jump we don't want it
if (!MiniArm64Decompiler.IsB(func[^1]))
continue;
var registers = MiniArm64Decompiler.GetAddressesLoadedIntoRegisters(func, (ulong)(_globalOffset + initializerPointer), this);
//Did we find the initializer defined in Il2CppCodeRegistration.cpp?
//It will have only x0 and x1 set.
if (registers.Count == 2 && registers.ContainsKey(0) && registers.TryGetValue(1, out var x1))
{
//Load the function whose address is in X1
var secondFunc = MiniArm64Decompiler.ReadFunctionAtRawAddress(this, (uint)MapVirtualAddressToRaw(x1), 7);
if (!MiniArm64Decompiler.IsB(secondFunc[^1]))
continue;
registers = MiniArm64Decompiler.GetAddressesLoadedIntoRegisters(secondFunc, x1, this);
}
//Do we have Il2CppCodegenRegistration?
//In v21 and later - which is the only range we support - we have X0 through X2 and only those.
//We want what's in x0 and x1. x2 is irrelevant.
if (registers.Count == 3 && registers.TryGetValue(0, out var x0) && registers.TryGetValue(1, out x1) && registers.ContainsKey(2))
{
LibLogger.VerboseNewline($"\t\tFound valid sequence of bytes for il2cpp initializer function at 0x{initializerPointer:X}.");
return (x0, x1);
}
//Fail, move on.
LibLogger.VerboseNewline($"\t\tInitializer function at 0x{initializerPointer:X} is probably NOT the il2cpp initializer - got {registers.Count} register values with keys {string.Join(", ", registers.Keys)}.");
}
return (0, 0);
}
private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegDefaultBehavior(Il2CppMetadata metadata)
{
var methodCount = metadata.methodDefs.Count(x => x.methodIndex >= 0);
var typeDefinitionsCount = metadata.TypeDefinitionCount;
LibLogger.VerboseNewline("Searching for il2cpp structures in an ELF binary using non-arch-specific method...");
var searcher = new BinarySearcher(this, metadata, methodCount, typeDefinitionsCount);
LibLogger.VerboseNewline("\tLooking for code reg (this might take a while)...");
var codeReg = metadata.MetadataVersion >= 24.2f ? searcher.FindCodeRegistrationPost2019() : searcher.FindCodeRegistrationPre2019();
LibLogger.VerboseNewline($"\tGot code reg 0x{codeReg:X}");
LibLogger.VerboseNewline($"\tLooking for meta reg ({(metadata.MetadataVersion >= 27f ? "post-27" : "pre-27")})...");
var metaReg = metadata.MetadataVersion >= 27f ? searcher.FindMetadataRegistrationPost24_5() : searcher.FindMetadataRegistrationPre24_5();
LibLogger.VerboseNewline($"\tGot meta reg 0x{metaReg:x}");
return (codeReg, metaReg);
}
public override long RawLength => _raw.Length;
public override long MapVirtualAddressToRaw(ulong addr, bool throwOnError = true)
{
var section = _elfProgramHeaderEntries.FirstOrDefault(x => addr >= x.VirtualAddress && addr < x.VirtualAddress + x.VirtualSize);
if (section == null)
if (throwOnError)
throw new InvalidOperationException($"No entry in the Elf PHT contains virtual address 0x{addr:X}");
else
return VirtToRawInvalidNoMatch;
if (addr >= section.VirtualAddress + section.RawSize)
if (throwOnError)
throw new InvalidOperationException(
$"Virtual address {addr:X} is located outside of the file-backed portion of Elf PHT section at 0x{section.VirtualAddress:X}");
else
return VirtToRawInvalidOutOfBounds;
return (long)(addr - (section.VirtualAddress - section.RawAddress));
}
public override ulong MapRawAddressToVirtual(uint offset, bool throwOnError = true)
{
if (relocationBlocks.Any(b => b.start <= offset && b.end > offset))
if (throwOnError)
throw new InvalidOperationException("Attempt to map a relocation block to a virtual address");
else
return 0;
var section = _elfProgramHeaderEntries.FirstOrDefault(x => offset >= x.RawAddress && offset < x.RawAddress + x.RawSize);
if (section == null)
if (throwOnError)
throw new InvalidOperationException($"No entry in the Elf PHT contains raw address 0x{offset:X}");
else
return 0;
return section.VirtualAddress + offset - section.RawAddress;
}
public override byte GetByteAtRawAddress(ulong addr) => _raw[addr];
public override ulong GetRva(ulong pointer) => (ulong)((long)pointer - _globalOffset);
public override ReadOnlySpan<byte> GetRawBinaryContent() => _raw;
public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind)
{
if (!_exportNameTable.TryGetValue(toFind, out var exportedSymbol))
return 0;
return exportedSymbol.VirtualAddress;
}
public override bool IsExportedFunction(ulong addr) => _exportAddressTable.ContainsKey(addr);
public override bool TryGetExportedFunctionName(ulong addr, [NotNullWhen(true)] out string? name)
{
if (_exportAddressTable.TryGetValue(addr, out var symbol))
{
name = symbol.Name;
return true;
}
else
{
return base.TryGetExportedFunctionName(addr, out name);
}
}
public override IEnumerable<KeyValuePair<string, ulong>> GetExportedFunctions()
{
return _exportNameTable.Select(kv => new KeyValuePair<string, ulong>(kv.Key, kv.Value.VirtualAddress));
}
public override ulong GetVirtualAddressOfPrimaryExecutableSection() => _elfSectionHeaderEntries.FirstOrDefault(s => s.Name == ".text")?.VirtualAddress ?? 0;
public override ReadOnlySpan<byte> GetEntirePrimaryExecutableSection()
{
var primarySection = _elfSectionHeaderEntries.FirstOrDefault(s => s.Name == ".text");
if (primarySection == null)
return ReadOnlySpan<byte>.Empty;
return GetRawBinaryContent().Slice((int)primarySection.RawAddress, (int)primarySection.Size);
}
}