80 lines
2.1 KiB
Python
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()
|