mirror of https://github.com/zeldaret/botw.git
93 lines
3.2 KiB
Python
Executable File
93 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from typing import Dict, List
|
|
import argparse
|
|
|
|
import cxxfilt
|
|
from colorama import Fore
|
|
|
|
from util import utils, checker, elf
|
|
|
|
|
|
class Checker(checker.FunctionChecker):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.checking = ""
|
|
self.invalid_call_descriptions: List[str] = []
|
|
self.addr_to_symbol = elf.build_addr_to_symbol_table(elf.my_symtab)
|
|
self._possible_calls: Dict[int, int] = dict()
|
|
|
|
def reset(self) -> None:
|
|
self._possible_calls.clear()
|
|
|
|
def get_possible_calls(self) -> Dict[int, int]:
|
|
return self._possible_calls
|
|
|
|
def on_unknown_fn_call(self, orig_addr: int, decomp_addr: int):
|
|
existing_addr = self._possible_calls.get(orig_addr)
|
|
if existing_addr is not None and existing_addr != decomp_addr:
|
|
self.invalid_call_descriptions.append(
|
|
f"{orig_addr | 0x7100000000:#x} was mapped to {self.addr_to_symbol[existing_addr]} "
|
|
f"({existing_addr:#x}) "
|
|
f"but now maps to {self.addr_to_symbol[decomp_addr]} ({decomp_addr:#x})"
|
|
f" (while checking {self.checking})")
|
|
return
|
|
self._possible_calls[orig_addr] = decomp_addr
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser("Identifies matching functions by looking at function calls in matching functions")
|
|
parser.add_argument("-f", "--fn", help="Functions to analyze", nargs="*")
|
|
args = parser.parse_args()
|
|
|
|
functions_to_analyze = set(args.fn) if args.fn else set()
|
|
|
|
functions_by_addr: Dict[int, utils.FunctionInfo] = {fn.addr: fn for fn in utils.get_functions()}
|
|
fn_checker = Checker()
|
|
for fn in functions_by_addr.values():
|
|
if functions_to_analyze and fn.decomp_name not in functions_to_analyze:
|
|
continue
|
|
|
|
if fn.status != utils.FunctionStatus.Matching:
|
|
continue
|
|
|
|
base_fn = elf.get_fn_from_base_elf(fn.addr, fn.size)
|
|
try:
|
|
my_fn = elf.get_fn_from_my_elf(fn.decomp_name)
|
|
except KeyError:
|
|
utils.warn(f"could not find function {fn.decomp_name}")
|
|
continue
|
|
|
|
fn_checker.checking = fn.decomp_name
|
|
fn_checker.check(base_fn, my_fn)
|
|
|
|
if fn_checker.invalid_call_descriptions:
|
|
for x in fn_checker.invalid_call_descriptions:
|
|
utils.print_note(x)
|
|
utils.fail("invalid calls detected")
|
|
|
|
new_matches: Dict[int, str] = dict()
|
|
calls = fn_checker.get_possible_calls().copy()
|
|
for base_target, my_target in calls.items():
|
|
target_info = functions_by_addr.get(base_target)
|
|
if target_info is None:
|
|
continue
|
|
if target_info.status != utils.FunctionStatus.NotDecompiled:
|
|
continue
|
|
|
|
base_fn = elf.get_fn_from_base_elf(target_info.addr, target_info.size)
|
|
try:
|
|
name = fn_checker.addr_to_symbol[my_target]
|
|
my_fn = elf.get_fn_from_my_elf(name)
|
|
except KeyError:
|
|
continue
|
|
|
|
if fn_checker.check(base_fn, my_fn):
|
|
new_matches[base_target] = name
|
|
utils.print_note(f"new match: {Fore.BLUE}{cxxfilt.demangle(name)}{Fore.RESET}")
|
|
|
|
utils.add_decompiled_functions(new_matches)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|