luma/elftoexe.py
2025-05-22 22:16:46 +03:00

108 lines
3.0 KiB
Python
Executable File

#!/usr/bin/env python3
from elftools.elf.elffile import ELFFile
from elftools.elf.relocation import RelocationSection
import struct, sys, itertools
from dataclasses import dataclass
from collections import namedtuple
def bitsize(i):
return max(len(bin(i)[2:]), 1)
@dataclass
class X:
id: int
children: "any type"
def dump(x):
data = b''.join(dump(c) for c in x.children) if type(x.children) == list else x.children
l = len(data)
l = l | (1 << ((bitsize(l) + 6) // 7 * 7))
return x.id.to_bytes(length = (bitsize(x.id) + 7) // 8) + l.to_bytes(length = (bitsize(l) + 7) // 8) + data
ARGS = {}
for s in sys.argv[3:]:
k, v = s.split("=", 1)
ARGS[k] = v
SYMTRANSLATION = {}
HEADER = X(0x1A45DFA3, [X(0x4282, b'BDexeF')])
OUTPUT = X(0x10000000, [])
@dataclass
class ELFReloc:
offset: int
reference: int
@dataclass
class ELFSegment:
data: bytearray
vaddr: int
relocations: list[ELFReloc]
with open(sys.argv[1], "rb") as inp, open(sys.argv[2], "wb") as outp:
elf = ELFFile(inp)
segments = []
sections_base = min([sec["sh_offset"] for sec in elf.iter_sections() if sec.name])
base_vaddr = min([seg["p_vaddr"] for seg in elf.iter_segments()])
for seg in elf.iter_segments():
if seg["p_type"] != "PT_LOAD":
continue
# If no section is in the segment, then ignore it
if seg["p_offset"] < sections_base and seg["p_offset"] + seg["p_filesz"] < sections_base:
continue
inp.seek(seg["p_offset"])
segments.append(ELFSegment(vaddr = seg["p_vaddr"], data = bytearray(inp.read(seg["p_filesz"]) + b'\0' * (seg["p_memsz"] - seg["p_filesz"])), relocations = []))
rel_section = elf.get_section_by_name(".rel.dyn")
if rel_section:
for rel in rel_section.iter_relocations():
assert rel["r_info"] % 256 == 8
# The segment in which the relocation is found/required
seg_in = next((s for s in reversed(segments) if s.vaddr <= rel["r_offset"]), None)
offset = rel["r_offset"] - seg_in.vaddr
val = struct.unpack("I", seg_in.data[offset : offset + 4])[0]
# The segment pointed to by the relocation
seg_ref = next((s for s in reversed(segments) if s.vaddr <= val), None)
seg_in.data[offset : offset + 4] = struct.pack("I", val - seg_ref.vaddr)
seg_in.relocations.append(ELFReloc(offset = rel["r_offset"] - seg_in.vaddr, reference = segments.index(seg_ref)))
for seg in segments:
OUTPUT.children.append(X(0x6200, [ #Segment
X(0x6201, seg.data), #Segment data
X(0x6202, b''.join(struct.pack("IB", rel.offset, rel.reference) for rel in seg.relocations)) #Segment relocations
]))
for pub, sym in [(False, s) for s in elf.get_section_by_name(".symtab").iter_symbols()] + [(True, s) for s in elf.get_section_by_name(".dynsym").iter_symbols()]:
if not sym.name:
continue
seg = next((s for s in reversed(segments) if s.vaddr <= sym["st_value"]), None)
if not seg:
continue
OUTPUT.children.append(X(0xC0, [
X(0xC1, sym.name.encode()),
X(0xC2, struct.pack("IB", sym["st_value"] - seg.vaddr, segments.index(seg) | (128 if pub else 0)))
]))
outp.write(dump(HEADER))
outp.write(dump(OUTPUT))