#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2026 Richard Qin import argparse import struct import sys ELF_MAGIC = b"\x7fELF" ELFCLASS64 = 2 ELFDATA2LSB = 1 EM_RISCV = 243 SHT_SYMTAB = 2 SHT_RELA = 4 PT_LOAD = 1 R_RISCV_64 = 2 R_RISCV_RELATIVE = 3 RELOC_MAGIC = 0x4C554D45524C4F43 # "LUMERLOC" RELOC_VERSION = 1 RELOC_ARCH_RISCV = 1 def read_struct(fmt, data, offset): size = struct.calcsize(fmt) return struct.unpack_from(fmt, data, offset), size def parse_elf_header(data): if data[:4] != ELF_MAGIC: raise ValueError("not an ELF file") ei_class = data[4] ei_data = data[5] if ei_class != ELFCLASS64 or ei_data != ELFDATA2LSB: raise ValueError("unsupported ELF class/data") fmt = "<16sHHIQQQIHHHHHH" (e_ident, e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx), _ = read_struct(fmt, data, 0) return { "e_machine": e_machine, "e_phoff": e_phoff, "e_phentsize": e_phentsize, "e_phnum": e_phnum, "e_shoff": e_shoff, "e_shentsize": e_shentsize, "e_shnum": e_shnum, "e_shstrndx": e_shstrndx, } def parse_program_headers(data, ehdr): phdrs = [] fmt = "= start and vaddr + size <= end: return True return False def main(): ap = argparse.ArgumentParser() ap.add_argument("--elf", required=True) ap.add_argument("--out", required=True) args = ap.parse_args() data = open(args.elf, "rb").read() ehdr = parse_elf_header(data) if ehdr["e_machine"] != EM_RISCV: raise ValueError("only EM_RISCV supported") phdrs = parse_program_headers(data, ehdr) shdrs = parse_section_headers(data, ehdr) link_base = find_symbol(data, shdrs, "_start") if link_base is None: raise ValueError("_start symbol not found") image_end = find_symbol(data, shdrs, "__lume_image_end") if image_end is None: raise ValueError("__lume_image_end symbol not found") image_size = image_end - link_base entries = [] for sh in shdrs: if sh["sh_type"] != SHT_RELA: continue if sh["sh_link"] >= len(shdrs): continue symtab = read_symtab(data, shdrs, sh["sh_link"]) entsize = sh["sh_entsize"] or 24 count = sh["sh_size"] // entsize for i in range(count): off = sh["sh_offset"] + i * entsize (r_offset, r_info, r_addend), _ = read_struct("> 32 if r_type not in (R_RISCV_RELATIVE, R_RISCV_64): continue if r_offset < link_base or r_offset >= link_base + image_size: continue if not vaddr_in_load(r_offset, phdrs, 8): continue ent_offset = r_offset - link_base if r_type == R_RISCV_RELATIVE: ent_addend = r_addend ent_type = 1 else: sym_val = 0 if r_sym < len(symtab): sym_val = symtab[r_sym][0] ent_addend = sym_val + r_addend if ent_addend < link_base or ent_addend >= link_base + image_size: continue ent_type = 2 entries.append((ent_offset, ent_addend, ent_type)) header = struct.pack( "