Files
Mulberry/tools/python/fix_bin_matching.py
T
2026-07-12 15:06:31 -05:00

80 lines
2.1 KiB
Python

from __future__ import annotations
import os
import argparse
import pydantic
from pathlib import Path
from typing import Protocol, cast
CONFIG_ROOT = Path(__file__).parent.parent.parent.resolve() / "config"
# enumerate languages from config dir
LANGUAGES = [Path(f.path).name for f in os.scandir(CONFIG_ROOT) if f.is_dir()]
class BytePatch(pydantic.RootModel[tuple[int, int]]):
root: tuple[int, int]
@property
def address(self):
return self.root[0]
@property
def size(self):
return self.root[1]
class BINPatch(pydantic.RootModel[dict[str, BytePatch]]):
root: dict[str, BytePatch]
class PatchDB(pydantic.RootModel[dict[str, BINPatch]]):
root: dict[str, BINPatch]
def fix_elf(orig_elf_path: Path, built_elf_path: Path, bin_patch: BytePatch):
with orig_elf_path.open("rb") as fh:
fh.seek(bin_patch.address)
data = fh.read(bin_patch.size)
with built_elf_path.open(mode="r+b") as wh:
wh.seek(bin_patch.address)
wh.write(data)
def main():
class ArgsProtocol(Protocol):
language: str
elf_file: Path
parser = argparse.ArgumentParser(description="apply asm patches to assembly files")
parser.add_argument("language", type=str, choices=LANGUAGES, help="language of the elf that is being patched")
parser.add_argument("elf_file", type=Path, help="elf file to patch (relative to build dir)")
args = cast(ArgsProtocol, parser.parse_args())
built_elf_file = CONFIG_ROOT / args.language / args.elf_file
if not built_elf_file.exists():
print(f"ERROR: cannot find elf file {built_elf_file}")
exit(1)
orig_elf_path = CONFIG_ROOT / args.language / args.elf_file.name
patch_db_path = CONFIG_ROOT / args.language / "bin_patches.json"
if not patch_db_path.exists():
exit(0)
patch_db = PatchDB.model_validate_json(patch_db_path.read_text())
for _tu_name, bin_patch in patch_db.root.items():
for _func_name, byte_patch in bin_patch.root.items():
fix_elf(orig_elf_path, built_elf_file, byte_patch)
if __name__ == "__main__":
main()