Files
Syphon_Filter_3/Makefile
T
Christopher Williams 091a9020f7 phase10: merge 16 + new worklist exclusion class — 453 bodies / 462 regions
+2 bodies (worker C claims 19-20) and 3 gp symbol rows (D_80121F90,
D_8012196E, D_80121964). Candidate gate MATCH before promotion.

NEW EXCLUSION CLASS `restores_unsaved` (worker A, coordinator-verified). A body
that restores a callee-saved register it never saves cannot be a whole function:
the register it restores was established by an enclosing prologue that the derived
extent cut off. These are jal targets INSIDE a real function, so the walk began
mid-body — distinct from bad_extent_start, which flags starts that are not
function entries at all.

Verified disjoint from the matched corpus before acting, as the project requires:
0 of 462 registered regions trip the rule; 11 worklist rows do.
excluded_restores_unsaved=11; worklist 1265 -> 1253 rows. 5 new tests; suite
229 -> 237, OK. make check green: regions=462 AGREE, differing_bytes=0 MATCH.

Independent corroboration worth recording: the new rule re-derives 0x8010080C,
the false extent start worker C reported earlier via a completely different signal
(the first instruction reads a register the range never defines). Two independent
detections of one defect class. bad_extent_start is False for that row, confirming
C's observation that the older rule missed it. Its Makefile --exclude entry is
retained only as the provenance record for that defect.
2026-09-24 07:49:34 -04:00

150 lines
5.5 KiB
Makefile

# Phase 3 all-assembly baseline orchestration.
# All original and generated ROM-derived inputs/outputs remain ignored.
SHELL := /usr/bin/bash
EXE := extracted/SCUS_946.40;1
MANIFEST := extracted/MANIFEST.tsv
SPLAT := tools/splat/.venv/bin/splat
AS := tools/mipsel-none-elf-binutils/prefix/usr/bin/mipsel-none-elf-as
LD := tools/mipsel-none-elf-binutils/prefix/usr/bin/mipsel-none-elf-ld
OBJCOPY := tools/mipsel-none-elf-binutils/prefix/usr/bin/mipsel-none-elf-objcopy
ASM_ROOT := asm
SPLAT_CONFIG := $(ASM_ROOT)/scus_946_40.yaml
SPLAT_OUTPUT := $(ASM_ROOT)/generated
LINKER_SCRIPT := $(ASM_ROOT)/scus_946_40.ld
BUILD := build
ELF := $(BUILD)/scus_946_40.elf
REBUILT := $(BUILD)/scus_946_40.rebuilt
INPUT_REPORT := .run/p3-exe-info.tsv
# Phase 5 ordered code build: C regions listed in the tracked registry are
# compiled with the identified toolchain and linked in address order between
# data fallbacks taken from the original executable. An empty registry
# reproduces the all-payload data baseline exactly.
REGIONS := config/regions.tsv
SYMBOLS := config/symbols.tsv
INVENTORY := config/function_inventory.tsv
EXTENTS := config/function_extents.tsv
DUPES := config/duplicate_bodies.tsv
NEGATIVES := config/near_match_negatives.tsv
WORKLIST := config/match_worklist.tsv
MATCH := tools/sf3_match
EXTENTS_TOOL := tools/sf3_extents
DUPES_TOOL := tools/sf3_dupes
TRIAGE_TOOL := tools/sf3_triage
CODE_OUT := build/code
EXPECTED_SHA1 := e173426c157384ebf1b6caf8c6fea18a85a14af9
.PHONY: all validate split assemble link binary code gate extents extents-verify dupes worklist test check clean
all: binary
validate:
@test -f "$(EXE)"
@test -f "$(MANIFEST)"
@mkdir -p .run
@./tools/sf3_extract psx-exe-info "$(EXE)" "$(MANIFEST)" > "$(INPUT_REPORT)"
split: validate
@test -f "$(SPLAT)"
@test -f "$(SPLAT_CONFIG)"
@cd "$(ASM_ROOT)" && ../"$(SPLAT)" split "$$(basename "$(SPLAT_CONFIG)")" --disassemble-all
assemble: split
@test -d "$(SPLAT_OUTPUT)" && test ! -L "$(SPLAT_OUTPUT)"
@test -f "$(AS)"
@test ! -L "$(BUILD)"
@mkdir -p "$(BUILD)"
@while IFS= read -r source; do \
rel="$${source#$(ASM_ROOT)/}"; \
object="$(BUILD)/$${rel}.o"; \
mkdir -p "$$(dirname "$$object")"; \
"$(AS)" -march=r3000 -G0 -I "$(ASM_ROOT)/include" -o "$$object" "$$source"; \
done < <(find "$(SPLAT_OUTPUT)" -type f -name '*.s' -print | sort)
link: assemble
@test -f "$(LINKER_SCRIPT)"
@test -f "$(LD)"
@rm -f "$(ELF)"
@"$(LD)" -T "$(LINKER_SCRIPT)" -o "$(ELF)"
binary: link
@test -f "$(OBJCOPY)"
@rm -f "$(REBUILT)"
@"$(OBJCOPY)" -O binary "$(ELF)" "$(REBUILT)"
# Ordered code build and full-binary gate (Phase 5). `code` builds only;
# `gate` builds and then requires the whole executable to be byte-identical.
code: validate
@test -f "$(MATCH)"
@test -f "$(REGIONS)"
@test -f "$(SYMBOLS)"
@rm -rf "$(CODE_OUT)"
@"$(MATCH)" build --exe "$(EXE)" --regions "$(REGIONS)" --symbols "$(SYMBOLS)" --out "$(CODE_OUT)"
gate: validate
@test -f "$(MATCH)"
@test -f "$(REGIONS)"
@test -f "$(SYMBOLS)"
@rm -rf "$(CODE_OUT)"
@"$(MATCH)" gate --exe "$(EXE)" --regions "$(REGIONS)" --symbols "$(SYMBOLS)" --out "$(CODE_OUT)" \
--expect-sha1 "$(EXPECTED_SHA1)"
# Function extents (Phase 7). `extents` regenerates the tracked table from the
# inventory; `extents-verify` re-derives it and requires every registered region
# to agree, so a region can never silently disagree with the derived extent.
extents: validate
@test -f "$(EXTENTS_TOOL)"
@test -f "$(INVENTORY)"
@"$(EXTENTS_TOOL)" scan --exe "$(EXE)" --inventory "$(INVENTORY)" --out "$(EXTENTS)" --force
extents-verify: validate
@test -f "$(EXTENTS_TOOL)"
@test -f "$(INVENTORY)"
@test -f "$(EXTENTS)"
@"$(EXTENTS_TOOL)" verify --exe "$(EXE)" --inventory "$(INVENTORY)" \
--extents "$(EXTENTS)" --regions "$(REGIONS)"
# Duplicate-body census (Phase 7): groups byte-identical bodies so a shared body
# is matched once and registered once per address.
dupes: validate
@test -f "$(DUPES_TOOL)"
@test -f "$(EXTENTS)"
@"$(DUPES_TOOL)" census --exe "$(EXE)" --extents "$(EXTENTS)" --out "$(DUPES)" --force
# Ranked match worklist (Phase 7). The --exclude addresses are recorded
# near-misses or known-bad rows deferred by developer direction; naming them here
# keeps the exclusion visible instead of burying it in the tool.
# 0x8010080C was added in Phase 10 as a FALSE EXTENT START (worker C): its first
# instruction is `beq s0,zero` with s0 never written in range, and the walk split
# one real 252-byte function (0x801007E0..0x801008DC) at a spurious `jal`-target
# boundary. It is now caught earlier by sf3_triage's general `restores_unsaved`
# rule (worker A's independent scan re-derived the same row from a different
# signal), so this row is retained only as the provenance record for that defect.
worklist: validate
@test -f "$(TRIAGE_TOOL)"
@test -f "$(EXTENTS)"
@test -f "$(DUPES)"
@"$(TRIAGE_TOOL)" plan --exe "$(EXE)" --extents "$(EXTENTS)" --inventory "$(INVENTORY)" \
--census "$(DUPES)" --regions "$(REGIONS)" --negatives "$(NEGATIVES)" \
--exclude 0x8005DEF8 --exclude 0x800F3160 \
--exclude 0x80108034 --exclude 0x8010804C \
--exclude 0x80012A10 --exclude 0x80012AE0 \
--exclude 0x80012CFC --exclude 0x80012B20 \
--exclude 0x8008A198 --exclude 0x8010080C --out "$(WORKLIST)" --force
# Verification gates. `test` is synthetic-only and needs no game input;
# `check` adds the extents check and the full-binary byte gate (which do).
test:
@PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tools/tests
check: test extents-verify gate
clean:
@if test -e "$(BUILD)"; then \
test -d "$(BUILD)" && test ! -L "$(BUILD)"; \
rm -rf "$(BUILD)"; \
fi