mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 05:56:00 -04:00
5984749421
- tools/masked_diff.py: shared objdump -drz masking oracle (jal/j 26-bit + HI16/LO16 immediate mask; -z keeps nop runs -> no GTE under-count). object-vs-object (permuter, +reloc-operand equality) and object-vs-.s (match_one) modes. - match_one.py refactored onto masked_diff (-dr -> -drz); regression-clean on count-exact seeds (35/52/72/110, leaf-MATCHes), output format unchanged (gate_stage/grinder compatible) - tools/masked_scorer.py MaskedScorer: drop-in for decomp-permuter's Scorer; scores masked .text closeness (bottoms out at 0) not the stock mnemonic-diff floor - tools/permuter/run_masked.py: rebinds src.main.Scorer -> MaskedScorer (NO submodule edit, R3/R20); p16_permute.run_permuter wired to it - VALIDATED: masked_self=0; masked_cand~match_one; STOCK floor 1750-2100 vs masked 36-77 (the wander cause); live permuter base score = masked 77 (not stock 1930), descends to 74 - R14 SELF-CORRECTION: -drz confirms func_80132784 is 4/400 count-exact (T1's -dr 204 was a 16-nop-collapse artifact); backlog re-logged close 4. The flagship IS 4 ins away. - no build-input changed (136/136 untouched); 2 compile FAILs surfaced -> T3 (GTE asm, typedefs)
49 lines
2.4 KiB
Python
49 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""masked_scorer.py — a drop-in for decomp-permuter's Scorer that scores relocation-masked .text
|
|
closeness (Phase 24 T2). Rebound over src.main.Scorer by tools/permuter/run_masked.py.
|
|
|
|
The stock Scorer diffs objdump MNEMONIC sequences and symbolizes relocations, penalizing any
|
|
symbol/addend divergence (scorer.py PENALTY_REGALLOC/REORDERING/INSERTION). For BFM's dotless
|
|
symbols (func_800…/D_800…) its `field_matches_any_symbol` wildcard never fires, so the score floats
|
|
on a nonzero FLOOR the search can't cross (func_80176D94 base~225/best~210/never 0) — the random walk
|
|
diverges. This scorer instead returns masked_diff.diff_object_object(cand, target): same-count,
|
|
non-reloc words identical, reloc/jal slots matching opcode+regs AND symbol+addend. It bottoms out at
|
|
exactly 0 ⟺ the two functions link to identical bytes, giving the search a gradient that reaches 0.
|
|
|
|
Same contract as Scorer: __init__(target_o, *, stack_differences, algorithm, debug_mode);
|
|
score(cand_o)->(int,str); PENALTY_INF. NOT the byte-gate — the whole-binary rebuild (harvest_verify)
|
|
is the sole arbiter (G3/P9); a permuter output-0 is a strong CANDIDATE to gate, not a bank.
|
|
"""
|
|
import hashlib
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import masked_diff
|
|
|
|
|
|
class MaskedScorer:
|
|
PENALTY_INF = 10 ** 9
|
|
|
|
def __init__(self, target_o, *, stack_differences=False, algorithm="", debug_mode=False):
|
|
# target.o and cand.o each hold exactly ONE function (p16_permute assembles/compiles one),
|
|
# so whole-.text is unambiguous — no function-name plumbing needed.
|
|
self.target_o = target_o
|
|
self.debug_mode = debug_mode
|
|
self.target = masked_diff.insns_from_object(target_o)
|
|
|
|
def score(self, cand_o):
|
|
if not cand_o:
|
|
return (self.PENALTY_INF, "")
|
|
cand = masked_diff.insns_from_object(cand_o)
|
|
if not cand:
|
|
return (self.PENALTY_INF, "")
|
|
d = masked_diff.diff_object_object(cand, self.target)
|
|
# deterministic content hash of the candidate's asm (words + relocs) so the permuter dedups
|
|
# identical candidates — mirrors the stock Scorer's "hash the objdump output" second field.
|
|
h = hashlib.sha256(
|
|
"".join(f"{i['word']:08x}:{i['reloc_kind'] or ''}:{i['reloc_op'] or ''}"
|
|
for i in cand).encode()
|
|
).hexdigest()
|
|
return (d, h)
|