#!/usr/bin/env python3 """ gen_pe_reloc.py — Prepare a RISC-V ELF shared library for PE/EFI conversion. Usage: gen_pe_reloc.py Problem: When the GNU linker builds a shared ELF with -shared at base 0, it leaves R_RISCV_RELATIVE target locations ZEROED. The ELF dynamic linker fills them at load time via: *target = base + r_addend But a PE IMAGE_REL_BASED_DIR64 entry does: *target += delta so if *target starts at 0 the result is just delta, not addend + delta. Fix (done here): 1. Parse .rela.dyn to get all R_RISCV_RELATIVE (offset, addend) pairs. 2. Pre-fill each target location in the ELF with its addend value. Now *target = addend, so PE relocation produces addend + delta. ✓ 3. Write the patched ELF to . 4. Build a PE IMAGE_BASE_RELOCATION table from the offsets and write it to . This is later injected as the .reloc section. """ import struct import sys from collections import defaultdict def parse_segments(elf_data): """Return list of (p_type, p_offset, p_vaddr, p_filesz) from PT_LOAD.""" e_phoff = struct.unpack_from(' ", file=sys.stderr) sys.exit(1) elf_data = bytearray(open(sys.argv[1], 'rb').read()) assert elf_data[:4] == b'\x7fELF', "Not an ELF file" assert elf_data[4] == 2 and elf_data[5] == 1, "Need 64-bit LE ELF" segs = parse_segments(elf_data) pairs = parse_rela_dyn(elf_data) patched = 0 skipped = 0 for r_offset, r_addend in pairs: file_off = vma_to_fileoff(segs, r_offset) if file_off is None: skipped += 1 continue # Pre-fill the 8-byte slot with the addend so that # PE DIR64 relocation (+=delta) produces addend+delta. struct.pack_into('