From 78102200c3ca3731b761ef54cb5863747ab5488f Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Wed, 13 May 2026 16:00:10 +0800 Subject: feat: add gen_reloc script for ELF relocation processing --- na/scripts/gen_reloc.py | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 na/scripts/gen_reloc.py (limited to 'na/scripts') diff --git a/na/scripts/gen_reloc.py b/na/scripts/gen_reloc.py new file mode 100644 index 0000000..6709e17 --- /dev/null +++ b/na/scripts/gen_reloc.py @@ -0,0 +1,224 @@ +#!/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( + "