Files
BFM-decomp/cookbook/C0209.md
T

2.5 KiB

§187 — 🔴 "SAME SOURCE" IS NOT "SAME OBJECT": THE SDK BUILD AND THE GAME BUILD DISAGREE ON GTE NOPS

The claim. A wave-S agent found that src/800b_7.c is not game code at all: its eight symbols are GsSortBg + GsSortFastBg, i.e. PsyQ libgs 2D_BG0.o / 2D_BG1.o. It reported "526 ins, 0 relocation-masked diffs" and recommended banking all 1,022 instructions for free through the existing psyq_integrate machinery instead of decompiling them. The reasoning was excellent and the symbol names really do encode the SDK objects (gfx2D_BG0_OBJ_4D8 = "2D_BG0.OBJ + 0x4D8").

The refutation, in three steps — each stricter than the last:

oracle verdict
the agent's masked_diff comparison 0 diffs
my re-check, excusing any word that carries a relocation 0 unexplained
psyq_identify's objdump pattern, masking only the relocated FIELD 271 mismatches

The third is the one that matters, and its diff is diagnostic: the object's stream is 520 instructions where the EXE region is 526, and the mismatches begin as a one-instruction SHIFT around COP2 words (0x005E001A, 0x17C00002). The game's copy carries six extra GTE hazard nops that the SDK object does not. Same C source, different assembly — so the object can never fill the region byte-identically, and psyq_identify was right to refuse it as "not linked by EXE".

THE LESSON IS ABOUT THE ORACLE, NOT THE LIBRARY. Two of the three checks agreed with each other and were both wrong, because both were permissive in the same way: they excused a whole instruction whenever it carried a relocation. A relocation licenses only its own FIELD to differ — 16 bits for HI16/LO16, 26 for a jump target — never the opcode or the register operands. A masked comparison whose mask is coarser than the linker's is not evidence of a match; it is evidence of nothing. Agreement between two loose checks is not corroboration (R34: oracles must be able to disagree).

What this costs and what it saves. It costs the 1,022-instruction shortcut: those eight symbols stay decompilation work, and GsSortBg/GsSortFastBg remain fragment-merge targets (§181's mirror class — five interior j targets each). It saves the far larger error of shipping a linked region that is six instructions short, which would have failed the byte gate and been debugged as a linker problem. Check the strictest available oracle BEFORE re-architecting around a byte claim (R35).