From 181ed5cccd7222b8725b25c1879448e67e3ec4c3 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 5 Jun 2024 10:49:48 +0200 Subject: [PATCH] fxsdk: add ELF patch mechanism for HH2 to specify filename for stage-2 --- fxsdk/cmake/GenerateHH2Bin.cmake | 4 +- fxsdk/scripts/large_obj.py | 3 +- fxsdk/scripts/patch_hh2_filename.py | 90 +++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100755 fxsdk/scripts/patch_hh2_filename.py diff --git a/fxsdk/cmake/GenerateHH2Bin.cmake b/fxsdk/cmake/GenerateHH2Bin.cmake index ae77af5..e5095e3 100644 --- a/fxsdk/cmake/GenerateHH2Bin.cmake +++ b/fxsdk/cmake/GenerateHH2Bin.cmake @@ -29,8 +29,8 @@ function(generate_hh2_bin) add_custom_command( TARGET "${HH2_TARGET}" POST_BUILD + COMMAND fxsdk script patch_hh2_filename.py ${HH2_TARGET} ${HH2_OUTPUT} COMMAND ${OBJCOPY} -O binary -R .bss -R .gint_bss ${HH2_TARGET} ${HH2_OUTPUT} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - COMMAND_EXPAND_LISTS - ) + COMMAND_EXPAND_LISTS) endfunction() diff --git a/fxsdk/scripts/large_obj.py b/fxsdk/scripts/large_obj.py index 1b6cca4..0b03d17 100755 --- a/fxsdk/scripts/large_obj.py +++ b/fxsdk/scripts/large_obj.py @@ -8,7 +8,8 @@ import elftools.elf.elffile if len(sys.argv) != 2 or "-h" in sys.argv or "--help" in sys.argv: print(f"usage: {sys.argv[0]} ", file=sys.stderr) - print("Prints sections of *.o/*.obj files in the folder, sorted by size") + print("Prints sections of *.o/*.obj files in the folder, sorted by size", + file=sys.stderr) sys.exit(1) files = glob.glob(sys.argv[1] + "/**/*.o", recursive=True) diff --git a/fxsdk/scripts/patch_hh2_filename.py b/fxsdk/scripts/patch_hh2_filename.py new file mode 100755 index 0000000..c334517 --- /dev/null +++ b/fxsdk/scripts/patch_hh2_filename.py @@ -0,0 +1,90 @@ +#! /usr/bin/env python + +import subprocess +import struct +import sys +import os +import re + +eprint = lambda *args, **kwargs: print(*args, **kwargs, file=sys.stderr) +def error(*args, **kwargs): + eprint("error:", *args, **kwargs) + sys.exit(1) + +if len(sys.argv) != 3: + eprint(f"usage: {sys.argv[0]} ") + eprint("Patches filenames into HH2-targeted ELF files for stage-2 load") + sys.exit(1) + +# Check that the filename fits +path = sys.argv[1] +name = os.path.basename(sys.argv[2]) +encoded_name = bytes(name, "utf-8") +if len(encoded_name) > 31: + error(f"output filename {name} is too long (max. 31 bytes)") + +# Run a command and return its stdout +def cmd(command): + rc = subprocess.run(command, stdout=subprocess.PIPE, check=True) + return rc.stdout.decode("utf-8") + +# Get the list of addresses at which a symbol called "name" is defined +def getsym(path, name): + # All syms as a list of (address, type, name) + syms = [l.split(" ") for l in cmd(["sh-elf-nm", path]).splitlines()] + return [int(t[0], 16) for t in syms if t[2] == name] + +# Get the section info for the section that contains address "addr" +def getsection(path, addr): + RE_SECTION = re.compile( + r'^\s*\[\s*\d+\]\s*([\w._]+)\s+([A-Z]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)\s+([0-9a-fA-F]+)') + lines = cmd(["sh-elf-readelf", "-S", path]).splitlines() + + # Parse sections from the output of readelf + sections = [] + for l in lines: + m = RE_SECTION.match(l) + if m is None: + continue + sections.append((m[1], m[2], int(m[3], 16), int(m[4], 16), int(m[5], 16))) + + # Find sections that contain the given address + matches = [] + for name, kind, start, off, size in sections: + if kind == "PROGBITS" and start <= addr < start + size: + matches.append((name, off + (addr - start))) + + return matches + +# Get the address of symbol .stage2_filename +s = getsym(path, ".stage2_filename") +if len(s) == 0: + error(f"no symbol .stage2_filename, is this built for fx-CP?") +if len(s) > 1: + error(f"multiple ({len(s)}) symbols named .stage2_filename, what?") +filename_address = s[0] + +s = getsection(path, filename_address) +if len(s) == 0: + error(f".stage2_filename is not in any section?") +if len(s) > 1: + error(f"overlapping sections ({', '.join(e[0] for e in s)}), excuse me?") +if s[0][0] != ".hh2": + error("how is .stage2_filename not in the .hh2 section?") +name_offset = s[0][1] + +# Load file contents +with open(path, "rb+") as fp: + fp.seek(name_offset) + name_placeholder = struct.unpack(">8i", fp.read(32)) + if name_placeholder != tuple(range(1, 8+1)): + error("name placeholder is wrong, check binary format and ELF parsing") + + # Pad to 32 bytes + encoded_name += bytes(32 - len(encoded_name)) + assert len(encoded_name) == 32 + + fp.seek(name_offset) + fp.write(encoded_name) + +sys.exit(0)