mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 21:36:06 -04:00
0cda15c6de
Pre-existing latent breakage (Phase-21 close commit:0292, "+62 PsyQ names"), found during T5b's R22 fleet verify. A genuinely-clean `make clean` + re-extract + build of `main` failed with dozens of `can't open asm/nonmatchings/800c3/func_*.s` — clean-rebuild broken since Phase 21, masked all along by incremental builds reusing stale pre-rename .s/.o (the exact R22 failure mode). - Root cause (byte-proven): Phase-21 xdedup renamed 62 PsyQ library functions to their proper names (InitHeap, FlushCache, GetTPage, SysEnqIntRP, SpuWrite, CdMix, __main …) in symbols.us.txt, but never regenerated/renamed the committed src/*.c stub refs that call them by the OLD func_<ADDR> name. splat's FRESH regeneration of a stub .c uses the CURATED names (move src/800c3.c aside -> splat writes INCLUDE_ASM(InitHeap) + emits InitHeap.s); the committed stubs were simply stale. - Fix: rename all 62 INCLUDE_ASM(func_<ADDR>) -> the curated name across 12 files (800c.c 1, 800c3.c 22, apicard1/2/4 4/6/2, boot.c 1, libcd1.c 8, libetc.c 6, libgpu.c 3, sgap.c 1, snd1.c 7, snd2.c 1). Pure rename to match splat's canonical output; byte-neutral. - VERIFIED: main clean-builds 143dbb89 from a fully clean tree; full clean fleet check-all 136/136. - Lesson (cookbook): a symbols.us.txt rename must be propagated to (a) shared-macro bodies (engine_core.h — the T5b GetTPage fix) AND (b) INCLUDE_ASM stub refs (this), AND verified by a genuinely-clean check-all (make clean + full re-extract), never incremental. Added a lint check.
94 lines
4.2 KiB
Python
94 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""lint_symbol_refs.py — catch stale func_<ADDR> refs that a symbols.us.txt rename left dangling.
|
|
|
|
The Phase-24 T5b/T5c breakage class: Phase-21 xdedup renamed 62 PsyQ functions to their curated
|
|
names (InitHeap, GetTPage, SysEnqIntRP …) in config/symbols.us.txt, but the committed source that
|
|
references them by the OLD `func_<ADDR>` name was never updated — both `INCLUDE_ASM(func_<ADDR>)`
|
|
stub refs (src/*.c) AND `func_<ADDR>(...)` calls in shared macros (src/shared/engine_core.h). A
|
|
GENUINELY-clean rebuild then fails (splat emits `<curated>.s` / provides the symbol only under the
|
|
curated name), but INCREMENTAL builds reuse stale .s/.o and mask it (the R22 failure mode). This
|
|
went undetected from Phase 21 → 23.
|
|
|
|
This lint flags every `func_<ADDR>` token in committed src/ whose address has a CURATED (non-`func_`)
|
|
name in the symbol files and NO `func_<ADDR>` symbol of its own — i.e. a reference splat/the linker
|
|
can no longer resolve under that name. Run it after any symbols rename and in `make report`.
|
|
|
|
Exit 0 = clean; exit 1 = stale refs found (printed as file:line func_<ADDR> -> curated).
|
|
"""
|
|
import re, glob, os, sys
|
|
|
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
def load_symbols():
|
|
"""addr(lower hex) -> curated name (excludes the func_<ADDR>/D_<ADDR> auto-names); and the set
|
|
of addresses that DO have a literal auto-name symbol (those resolve under func_/D_ as written).
|
|
Covers both the func_ (code) and D_ (data) auto-name classes."""
|
|
curated, autosym = {}, set()
|
|
for sf in ("config/symbols.us.txt", "config/symbols.resident.txt"):
|
|
p = os.path.join(REPO, sf)
|
|
if not os.path.exists(p):
|
|
continue
|
|
for m in re.finditer(r'^([A-Za-z_]\w*)\s*=\s*(0x[0-9A-Fa-f]+)', open(p).read(), re.M):
|
|
nm, a = m.group(1), m.group(2).lower()
|
|
if nm.lower() in ("func_" + a[2:], "d_" + a[2:]):
|
|
autosym.add(a)
|
|
else:
|
|
curated.setdefault(a, nm)
|
|
return curated, autosym
|
|
|
|
|
|
def strip_comments_strings(src):
|
|
"""blank out /* */ + // comments and string/char-literal CONTENTS (keeping newlines so line
|
|
numbers stay correct), so a `func_<ADDR>` mentioned only in a comment/string isn't flagged.
|
|
A bare INCLUDE_ASM(func_<ADDR>) token lives OUTSIDE the quotes, so it survives."""
|
|
out = []
|
|
i, n = 0, len(src)
|
|
while i < n:
|
|
c = src[i]
|
|
two = src[i:i+2]
|
|
if two == "/*":
|
|
j = src.find("*/", i + 2)
|
|
j = n if j < 0 else j + 2
|
|
out.append("".join(ch if ch == "\n" else " " for ch in src[i:j])); i = j
|
|
elif two == "//":
|
|
j = src.find("\n", i)
|
|
j = n if j < 0 else j
|
|
out.append(" " * (j - i)); i = j
|
|
elif c in '"\'':
|
|
q = c; j = i + 1
|
|
while j < n and src[j] != q:
|
|
j += 2 if src[j] == "\\" else 1
|
|
j = min(j + 1, n)
|
|
out.append(q + " " * (j - i - 2) + q if j - i >= 2 else src[i:j]); i = j
|
|
else:
|
|
out.append(c); i += 1
|
|
return "".join(out)
|
|
|
|
|
|
def main():
|
|
curated, autosym = load_symbols()
|
|
stale = []
|
|
for cf in sorted(glob.glob(os.path.join(REPO, "src/**/*.c"), recursive=True)):
|
|
rel = os.path.relpath(cf, REPO)
|
|
code = strip_comments_strings(open(cf, errors="replace").read())
|
|
for i, line in enumerate(code.splitlines(), 1):
|
|
for m in re.finditer(r'\b(?:func_|D_)([0-9A-Fa-f]{6,8})\b', line):
|
|
a = "0x" + m.group(1).lower()
|
|
if a in autosym: # a real func_/D_<ADDR> symbol exists -> resolves
|
|
continue
|
|
if a in curated: # renamed to a curated name, no auto-name symbol -> DANGLING
|
|
stale.append((rel, i, m.group(0), curated[a]))
|
|
if stale:
|
|
print(f"[lint_symbol_refs] {len(stale)} STALE func_<ADDR> ref(s) — renamed in symbols, "
|
|
f"clean-build will FAIL (fix: rename to the curated name):")
|
|
for rel, i, tok, nm in stale:
|
|
print(f" {rel}:{i} {tok} -> {nm}")
|
|
return 1
|
|
print("[lint_symbol_refs] OK — no stale renamed func_<ADDR> refs in committed src/")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|