Process ELF32 relocations
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Common;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -15,7 +16,48 @@ namespace Il2CppInspector
|
|||||||
{
|
{
|
||||||
internal class ElfReader : FileFormatReader<ElfReader>
|
internal class ElfReader : FileFormatReader<ElfReader>
|
||||||
{
|
{
|
||||||
private program_header_table[] program_header_table;
|
// Internal relocation entry helper
|
||||||
|
private struct ElfReloc
|
||||||
|
{
|
||||||
|
public Elf Type;
|
||||||
|
public uint Offset;
|
||||||
|
public uint? Addend;
|
||||||
|
public uint SymbolTable;
|
||||||
|
public uint SymbolIndex;
|
||||||
|
|
||||||
|
// Equality based on target address
|
||||||
|
public override bool Equals(object obj) => obj is ElfReloc reloc && Equals(reloc);
|
||||||
|
|
||||||
|
public bool Equals(ElfReloc other) {
|
||||||
|
return Offset == other.Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() {
|
||||||
|
unchecked {
|
||||||
|
var hashCode = (int)Type;
|
||||||
|
hashCode = (hashCode * 397) ^ (int)Offset;
|
||||||
|
hashCode = (hashCode * 397) ^ Addend.GetHashCode();
|
||||||
|
hashCode = (hashCode * 397) ^ (int)SymbolTable;
|
||||||
|
hashCode = (hashCode * 397) ^ (int)SymbolIndex;
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast operators (makes the below code MUCH easier to read)
|
||||||
|
public ElfReloc(elf_32_rel rel, uint symbolTable) {
|
||||||
|
Type = (Elf) (rel.r_info & 0xff);
|
||||||
|
Offset = rel.r_offset;
|
||||||
|
Addend = null;
|
||||||
|
SymbolIndex = rel.r_info >> 8; // r_info >> 8 is an index into the symbol table
|
||||||
|
SymbolTable = symbolTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElfReloc(elf_32_rela rela, uint symbolTable)
|
||||||
|
: this(new elf_32_rel { r_info = rela.r_info, r_offset = rela.r_offset }, symbolTable) =>
|
||||||
|
Addend = rela.r_addend;
|
||||||
|
}
|
||||||
|
|
||||||
|
private elf_32_phdr[] program_header_table;
|
||||||
private elf_32_shdr[] section_header_table;
|
private elf_32_shdr[] section_header_table;
|
||||||
private elf_32_dynamic[] dynamic_table;
|
private elf_32_dynamic[] dynamic_table;
|
||||||
private elf_header elf_header;
|
private elf_header elf_header;
|
||||||
@@ -35,7 +77,8 @@ namespace Il2CppInspector
|
|||||||
public override int Bits => (elf_header.m_arch == (uint) Elf.ELFCLASS64) ? 64 : 32;
|
public override int Bits => (elf_header.m_arch == (uint) Elf.ELFCLASS64) ? 64 : 32;
|
||||||
|
|
||||||
private elf_32_shdr getSection(Elf sectionIndex) => section_header_table.FirstOrDefault(x => x.sh_type == (uint) sectionIndex);
|
private elf_32_shdr getSection(Elf sectionIndex) => section_header_table.FirstOrDefault(x => x.sh_type == (uint) sectionIndex);
|
||||||
private program_header_table getProgramHeader(Elf programIndex) => program_header_table.FirstOrDefault(x => x.p_type == (uint) programIndex);
|
private IEnumerable<elf_32_shdr> getSections(Elf sectionIndex) => section_header_table.Where(x => x.sh_type == (uint)sectionIndex);
|
||||||
|
private elf_32_phdr getProgramHeader(Elf programIndex) => program_header_table.FirstOrDefault(x => x.p_type == (uint) programIndex);
|
||||||
private elf_32_dynamic getDynamic(Elf dynamicIndex) => dynamic_table?.FirstOrDefault(x => x.d_tag == (uint) dynamicIndex);
|
private elf_32_dynamic getDynamic(Elf dynamicIndex) => dynamic_table?.FirstOrDefault(x => x.d_tag == (uint) dynamicIndex);
|
||||||
|
|
||||||
protected override bool Init() {
|
protected override bool Init() {
|
||||||
@@ -51,10 +94,10 @@ namespace Il2CppInspector
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
program_header_table = ReadArray<program_header_table>(elf_header.e_phoff, elf_header.e_phnum);
|
program_header_table = ReadArray<elf_32_phdr>(elf_header.e_phoff, elf_header.e_phnum);
|
||||||
section_header_table = ReadArray<elf_32_shdr>(elf_header.e_shoff, elf_header.e_shnum);
|
section_header_table = ReadArray<elf_32_shdr>(elf_header.e_shoff, elf_header.e_shnum);
|
||||||
|
|
||||||
if (getProgramHeader(Elf.PT_DYNAMIC) is program_header_table PT_DYNAMIC)
|
if (getProgramHeader(Elf.PT_DYNAMIC) is elf_32_phdr PT_DYNAMIC)
|
||||||
dynamic_table = ReadArray<elf_32_dynamic>(PT_DYNAMIC.p_offset, (int) PT_DYNAMIC.p_filesz / 8 /* sizeof(elf_32_dynamic) */);
|
dynamic_table = ReadArray<elf_32_dynamic>(PT_DYNAMIC.p_offset, (int) PT_DYNAMIC.p_filesz / 8 /* sizeof(elf_32_dynamic) */);
|
||||||
|
|
||||||
// Get global offset table
|
// Get global offset table
|
||||||
@@ -63,8 +106,74 @@ namespace Il2CppInspector
|
|||||||
throw new InvalidOperationException("Unable to get GLOBAL_OFFSET_TABLE from PT_DYNAMIC");
|
throw new InvalidOperationException("Unable to get GLOBAL_OFFSET_TABLE from PT_DYNAMIC");
|
||||||
GlobalOffset = (uint) _GLOBAL_OFFSET_TABLE_;
|
GlobalOffset = (uint) _GLOBAL_OFFSET_TABLE_;
|
||||||
|
|
||||||
// TODO: Find all relocations
|
// Find all relocations; target address => (rela header (rels are converted to rela), symbol table base address, is rela?)
|
||||||
|
var rels = new HashSet<ElfReloc>();
|
||||||
|
|
||||||
|
// Two types: add value from offset in image, and add value from specified addend
|
||||||
|
foreach (var relSection in getSections(Elf.SHT_REL))
|
||||||
|
rels.UnionWith(
|
||||||
|
from rel in ReadArray<elf_32_rel>(relSection.sh_offset, (int) (relSection.sh_size / relSection.sh_entsize))
|
||||||
|
select new ElfReloc(rel, section_header_table[relSection.sh_link].sh_offset));
|
||||||
|
|
||||||
|
foreach (var relaSection in getSections(Elf.SHT_RELA))
|
||||||
|
rels.UnionWith(
|
||||||
|
from rela in ReadArray<elf_32_rela>(relaSection.sh_offset, (int)(relaSection.sh_size / relaSection.sh_entsize))
|
||||||
|
select new ElfReloc(rela, section_header_table[relaSection.sh_link].sh_offset));
|
||||||
|
|
||||||
|
// Relocations in dynamic section
|
||||||
|
if (getDynamic(Elf.DT_REL) is elf_32_dynamic dt_rel) {
|
||||||
|
var dt_rel_count = getDynamic(Elf.DT_RELSZ).d_un / getDynamic(Elf.DT_RELENT).d_un;
|
||||||
|
var dt_rel_list = ReadArray<elf_32_rel>(MapVATR(dt_rel.d_un), (int) dt_rel_count);
|
||||||
|
var dt_symtab = getDynamic(Elf.DT_SYMTAB).d_un;
|
||||||
|
rels.UnionWith(from rel in dt_rel_list select new ElfReloc(rel, dt_symtab));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getDynamic(Elf.DT_RELA) is elf_32_dynamic dt_rela) {
|
||||||
|
var dt_rela_count = getDynamic(Elf.DT_RELASZ).d_un / getDynamic(Elf.DT_RELAENT).d_un;
|
||||||
|
var dt_rela_list = ReadArray<elf_32_rela>(MapVATR(dt_rela.d_un), (int) dt_rela_count);
|
||||||
|
var dt_symtab = getDynamic(Elf.DT_SYMTAB).d_un;
|
||||||
|
rels.UnionWith(from rela in dt_rela_list select new ElfReloc(rela, dt_symtab));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process relocations
|
||||||
|
// WARNING: This modifies the stream passed in the constructor
|
||||||
|
if (BaseStream is FileStream)
|
||||||
|
throw new InvalidOperationException("Input stream to ElfReader is a file. Please supply a mutable stream source.");
|
||||||
|
|
||||||
|
var writer = new BinaryWriter(BaseStream);
|
||||||
|
|
||||||
|
foreach (var rel in rels) {
|
||||||
|
var symValue = ReadObject<elf_32_sym>(rel.SymbolTable + rel.SymbolIndex * 16 /* sizeof(elf_32_sym) */).st_value; // S
|
||||||
|
|
||||||
|
// The addend is specified in the struct for rela, and comes from the target location for rel
|
||||||
|
Position = MapVATR(rel.Offset);
|
||||||
|
var addend = rel.Addend ?? ReadUInt32(); // A
|
||||||
|
|
||||||
|
// Only handle relocation types we understand, skip the rest
|
||||||
|
// Relocation types from https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-54839.html#scrolltoc
|
||||||
|
// and https://studfiles.net/preview/429210/page:18/
|
||||||
|
(uint newValue, bool recognized) result = (rel.Type, (Elf) elf_header.e_machine) switch {
|
||||||
|
(Elf.R_ARM_ABS32, Elf.EM_ARM) => (symValue + addend, true), // S + A
|
||||||
|
(Elf.R_ARM_REL32, Elf.EM_ARM) => (symValue - rel.Offset + addend, true), // S - P + A
|
||||||
|
(Elf.R_ARM_COPY, Elf.EM_ARM) => (symValue, true), // S
|
||||||
|
|
||||||
|
(Elf.R_386_32, Elf.EM_386) => (symValue + addend, true), // S + A
|
||||||
|
(Elf.R_386_PC32, Elf.EM_386) => (symValue + addend - rel.Offset, true), // S + A - P
|
||||||
|
(Elf.R_386_GLOB_DAT, Elf.EM_386) => (symValue, true), // S
|
||||||
|
(Elf.R_386_JMP_SLOT, Elf.EM_386) => (symValue, true), // S
|
||||||
|
|
||||||
|
(Elf.R_AMD64_64, Elf.EM_AARCH64) => (symValue + addend, true), // S + A
|
||||||
|
|
||||||
|
_ => (0, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result.recognized) {
|
||||||
|
Position = MapVATR(rel.Offset);
|
||||||
|
writer.Write(result.newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Processed {rels.Count} relocations");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,13 +31,33 @@ namespace Il2CppInspector
|
|||||||
// SHTs
|
// SHTs
|
||||||
SHT_SYMTAB = 2,
|
SHT_SYMTAB = 2,
|
||||||
SHT_STRTAB = 3,
|
SHT_STRTAB = 3,
|
||||||
|
SHT_RELA = 4,
|
||||||
|
SHT_REL = 9,
|
||||||
SHT_DYNSYM = 11,
|
SHT_DYNSYM = 11,
|
||||||
|
|
||||||
// dynamic sections
|
// dynamic sections
|
||||||
DT_STRTAB = 5,
|
DT_STRTAB = 5,
|
||||||
DT_SYMTAB = 6,
|
DT_SYMTAB = 6,
|
||||||
|
DT_RELA = 7,
|
||||||
|
DT_RELASZ = 8,
|
||||||
|
DT_RELAENT = 9,
|
||||||
|
DT_REL = 17,
|
||||||
|
DT_RELSZ = 18,
|
||||||
|
DT_RELENT = 19,
|
||||||
DT_INIT_ARRAY = 25,
|
DT_INIT_ARRAY = 25,
|
||||||
DT_INIT_ARRAYSZ = 27
|
DT_INIT_ARRAYSZ = 27,
|
||||||
|
|
||||||
|
// relocation types
|
||||||
|
R_ARM_ABS32 = 2,
|
||||||
|
R_ARM_REL32 = 3,
|
||||||
|
R_ARM_COPY = 20,
|
||||||
|
|
||||||
|
R_386_32 = 1,
|
||||||
|
R_386_PC32 = 2,
|
||||||
|
R_386_GLOB_DAT = 6,
|
||||||
|
R_386_JMP_SLOT = 7,
|
||||||
|
|
||||||
|
R_AMD64_64 = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
@@ -90,7 +110,7 @@ namespace Il2CppInspector
|
|||||||
public ushort e_shtrndx;
|
public ushort e_shtrndx;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class program_header_table
|
internal class elf_32_phdr
|
||||||
{
|
{
|
||||||
public uint p_type;
|
public uint p_type;
|
||||||
public uint p_offset;
|
public uint p_offset;
|
||||||
@@ -132,5 +152,18 @@ namespace Il2CppInspector
|
|||||||
public uint d_tag;
|
public uint d_tag;
|
||||||
public uint d_un;
|
public uint d_un;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class elf_32_rel
|
||||||
|
{
|
||||||
|
public uint r_offset;
|
||||||
|
public uint r_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class elf_32_rela
|
||||||
|
{
|
||||||
|
public uint r_offset;
|
||||||
|
public uint r_info;
|
||||||
|
public uint r_addend;
|
||||||
|
}
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user