The §9.1 "scattered .bss commons" exclusion class (Phase 8 → P31) is closed 3/3. New tools/psyq_bss_split.py (own ELF32 REL reader/writer) cuts an object's packed .bss into per-base NOBITS pieces: bases derived from the game bytes per HI16/LO16 pair, references walked in offset order into single-base runs, cuts snapped to symbol starts (the linker scattered SYMBOLS), symbols moved, a LOCAL section symbol per piece inserted, relocs retargeted with the addend rewritten in the immediates, self-diffed. It runs inside the one prepare step shared by psyq_link.link_object / psyq_link_region.build_region / psyq_integrate.integrate (prepare_object before classify), re-derived every build. GS_001.o was certified "5 interleaved bases, NOT splittable" by the S77 probe, which grouped by BASE; by RUN it is six symbol-aligned pieces. All seven cuts across the three objects are confirmed by the other objects' by-name recoveries (_que 0x800C5510, _svm_sreg_buf 0x800B9B58, PSDBASEX/CLIP2/PSDBASEY/POSITION/GsDRAWENV). R39 negative control: 235 placed objects across 9 curated dirs, 0 refusals, exactly 3 splits (a libcd .bss+size end pointer refused the first build → reference problems are fatal only when a split is needed). Wiring: yaml 800c→libgpu2, sgap_6→sgap_6+snd12, gsgap3→libgs8 (comments rewritten); LIBGPU_ELF := .run/obj40/libgpu (curated libgpu_used retired); libgs 34 objs/8 blocks (make_libgs.sh +GS_001); snd 63/12 (make_snd_used.py exclusions 4→3). src/800c.c and src/gsgap3.c removed (Sony code hand-matched as REAL/verbatim), sgap_6.c keeps only func_8003FA54; splat-emitted libgpu2.c/libgs8.c/snd12.c stubs for the no-SDK fallback. Verified: main 143dbb89f34491258bbc27810d0a12ec8b43a8dd WITH the SDK objects and WITHOUT them from a fresh extract; make tools-health OK; R22 fleet clean extract-all 212/212 + check-all 213/213. Metrics: main REAL 886→839, LINKED 1,040→1,150, VERBATIM 85→29, stubs 29 (unchanged); game-code weighted 91.1% (40,895/44,870) — both terms lost the 3,667 SDK ins; the remainder is still exactly the 3,975-ins open-stub sum. Verbatim manifest --update 200→33 rows (subtractive). Docs: cookbook §489 (+index), psyq-worklist rows + "S78 task #4", SETUP S79 R21 table, decision-log S79 addendum, accelerators S79, CURRENT_PHASE S79 FINAL 🛑.
23 KiB
PsyQ library integration worklist (Phase 8)
Byte-confirmed footprint map of the remaining PsyQ 4.0 libraries linked by
SLUS_007.26. Generated bypsyq_identify.pyover the original EXE (extracted/retail/SLUS_007.26, text0x80010000..0x800629DC); raw per-object placements in.run/survey/<lib>.txt(regenerable:for L in …; do python3 tools/psyq_identify.py .run/obj40/$L --vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26; done). Replaces the Phase-7 ~350-fn estimate (R14). Placement = candidate footprint; per-object byte-identity is confirmed at integration time bypsyq_link_region.py --verifybefore wiring (the real gate, G3/P9).DONE (Gen1/Phase 7): libcd (18 objs) + libgs (31 objs) linked byte-identical.
Footprint summary (remaining libraries)
| Order | Library | Placed / total | Region(s) | ~banked B | Difficulty notes |
|---|---|---|---|---|---|
| 1 | libgpu | 3 / 12 | 800b2 (0x80058890) |
~14.9k | ✅ DONE (T4 + S78 #4): EXT+PRIM+SYS linked (raw .run/obj40/libgpu, no curated dir). SYS.o was EXCLUDED Phase 8→P31 S78 as scattered-.bss (§9.1); linked since S78 #4 via the link-prepare .bss split (cookbook §489) as libgpu2. |
| 2 | libetc | 5 / 7 | 800 tail (0x8004239C) |
~3.3k | VSYNC/INTR/INTR_VB/INTR_DMA/VMODE contiguous, ends exactly at libcd1 (0x80043088). Clean. |
| 3 | libmcrd | 2 / 2 | 800b2 (0x8005FC68,0x80062888) |
~9.0k | LIBMCRD.o (2186 ins, huge, holds the 55 LIBMCRD_OBJ_*) + USERFUNC.o — 2 blocks. _card_* h_norm dups collapse inside LIBMCRD. |
| 4 | libc2 | 17 / 46 | 800b2 (0x8005C2C8–0x5CD98 + STRCAT 0x80061E90) |
~3.1k | C stdlib (BZERO/MEMCPY/STRCMP/PRINTF/PRNT…); contiguous run + 1 outlier. PRNT.o (418 ins) has an internal jtbl — verify NOLOAD .rodata placement (the PRNT_OBJ_24C rodata note). |
| 5+6 | libapi+libcard | 22 (800c2) | 800c2 (0x80061F38–0x80062888) |
~0.7k | ✅ DONE (combined apicard region): 22 objs / 4 blocks / +24 fns (tools/make_apicard_used.py, C112 dedup, 0 exclusions). libapi's ~22 objects in the 800c3 region (0x5CE18..) DEFERRED — lowest value, separate resegmentation. |
| 7+8 | libspu+libsnd | 63 / (38+32) | 800 sound (0x8003A444–0x8004239C) |
~24k | ✅ DONE (combined region; S78 #3/#4): 63 objs / 12 blocks. The two libs interleave, so linked as ONE region (tools/make_snd_used.py dedups + excludes 3 addresses; gen_lib_subsegs.py + window). Excluded: S_R/S_W 0x3C438, S_GRMDT* 0x3D424 (cross-object commons — these objects have NO .bss, so the S78 split cannot apply), S_IH/UT_RON 0x3D94C (false-positive, inside SSSTART). VM_F 0x3FA64 (237 ins) rejoined in S78 #4 (snd12, .bss split at _svm_sreg_buf). SSGM.o 0x1BD80 deferred (isolated in matched-C, 8 ins). |
| 9 | libgte | 58 / 381 | 800b (0x8004787C–0x5082C) + libgs gaps |
~48k | ✅ DONE (T11): 53 objs / 22 blocks linked in 800b (subsegs via gen_lib_subsegs.py; integrate window 0x4787C..0x51804). 5 libgs-gap objects DEFERRED (MTX_05/07/11/REG03/REG11 → gsgap1/2/4/5 stay stubs; gsgap2≠MTX_07 exactly so needs sub-split). |
SKIP — zero footprint (recorded, not linked by the EXE): libmath 0/48, libc 0/56, libsn 0/51 (2 tiny ambiguous). BFM links libc2, not libc; no libmath/libsn. (Also unbuilt/no-footprint: libcomb, libds, libgun, libsio, libtap, libpress — never converted, no symbols.)
Region map (where each library lives, relative to the current splat subsegs)
800 (0x800123F0–0x80043088) game code + libsnd(SSGM @1BD80; 3D454–42374) + libspu(3A444–422E8) + libetc(4239C–43088, tail)
libcd (0x80043088–0x8004787C) DONE
800b (0x8004787C–0x80051804) libgte(4787C–5082C, multi-block) + game-code tail
libgs (0x8005080C–0x80057928) DONE; gaps gsgap1/2/4/5 are actually libgte (MTX_05/07/11, REG03/11; wired S78 #3 as libgte27-30); gsgap3=GS_001 linked S78 #4 as libgs8 (six-piece `.bss` split)
800b2 (0x80057928–0x800629DC) game code + libgpu(58890–5Bxxx) + libc2(5C2C8–5CD98,+STRCAT 61E90) + libapi(5CE18–626B8 scattered) + libmcrd(5FC68 + 62888) + libcard(61F38–62808)
Integration order rationale (ascending difficulty)
- 1–2 (libgpu, libetc): clean single contiguous blocks, minimal interleaving → re-validate the §9 mechanism end-to-end on a fresh library before the hard ones.
- 3–4 (libmcrd, libc2): few blocks, contained in 800b2; libc2 introduces the PRNT internal-jtbl wrinkle.
- 5–6 (libcard, libapi): short syscall stubs + cross-library aliases + scatter in 800b2 → need windowed placement + curated
_useddirs. - 7–8 (libspu, libsnd): the interleaved sound pair in the 800 subseg (which still holds the 43 REAL game-code matches lower down) →
split_src_region.pytrims, windowed placement, alias disambiguation. - 9 (libgte): largest, multi-region (800b + 4 libgs gaps), game-code-interleaved blocks → last.
Per-library integration recipe (each = one byte-gated task, §9.3/§9.5)
Phase 9: all these tools now REQUIRE
--vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26(the EXE's values;psyq_integratealso--symbols config/symbols.us.txt). For a Gen2 binary, pass its own--vram-base/--exe/--symbols(cookbook §9.7, SETUP §6.7).
psyq_link_region.py .run/obj40/<lib> <lo> <hi> --vram-base 0x8000F800 --exe extracted/retail/SLUS_007.26 [--emit <p>]→ confirm per-object byte-identity ("per-object .text byte-identical: True") + the block/gap structure (G1: derive blocks empirically, don't trust this table's addresses).- Curate
.run/obj40/<lib>_used(drop cross-library aliases / keep the variant whose.data/.rdataalso matches) when aliases exist (make_libgs.sh pattern). - Resegment
config/splat.us.exe.yaml(onecsubseg per contiguous block; non-library gaps stay stubs);split_src_region.py trimthe affected game-code.c;make extract. - Makefile: add
LIB<X>_ELF/OBJDIR/SYMS+ a gatedpsyq_integrate.py --vram-base $(main_VRAM_BASE) --exe $(main_EXE) --symbols $(main_SYMBOLS) … <stubs> [lo hi]call +-T $(LIB<X>_SYMS)on theldline. - Add recovered SDK names to
config/symbols.us.txt(mirror to Ghidra, R15/G6); update progress.py's LINKED config. - Dual byte-gate:
make clean && extract && build && check→143dbb89…WITH.run/obj40/<lib>present AND with it absent (stub fallback).make reportLINKED up. Commit (R20).
Cross-cutting: windowed placement is mandatory for the short-object / interleaved libraries (the libgs GS_106 precedent); cross-library address collisions (C112 libapi/libcard; UT_RON/S_IH; S_R/S_W) mean total distinct objects < the 199 summed placements — byte-verify decides ownership. Any object that won't link byte-identical after §9.1/§9.4 stays a documented honest stub (does not block exit, P9).
⚠️ Boundary gotcha (found at libc2/T6): the game-code subseg boundary AFTER a library block must be the last object's .text SECTION size (8-aligned), NOT its instruction count × 4. psyq_identify reports the instruction count, which omits the object's trailing 8-byte alignment padding (e.g. libc2 SETJMP.o: 30 ins = 0x78, but .text = 0x80 → block ends 8 bytes later). A too-low boundary overlaps the object's padded tail and the relink inserts +N padding, shifting the whole downstream image (pervasive 1-byte reloc diffs + grown file). Verify the boundary = last_obj.vram + readelf .text size for any library whose last object isn't followed object-to-object. Also: when a library block sits at a subseg's START (no < lo prefix to keep), rm the old .c so splat regenerates it under the new boundaries (split_src_region.trim would leave it header-only).
Excluded / deferred objects (§9.1 scattered-.bss) — honest stubs, byte-identical via asm
The standard mechanism places each object's .bss as ONE NOLOAD section at a single base. An object whose
.bss commons the original linker scattered to non-contiguous addresses (referenced as .bss+offset by a
single section symbol) cannot be reproduced by one base → excluded, kept as an INCLUDE_ASM stub (still
byte-identical). Diagnose: psyq_link_region.py <dir> shows N words differ in the object, and its .bss
references resolve to >1 base in the EXE. Curate the library's _used dir to drop these.
| Object | Library | Evidence | Status |
|---|---|---|---|
GS_001.o |
libgs | scattered .bss (Phase 7) — SIX bases |
LINKED S78 #4 (libgs8): psyq_bss_split cuts the section into six single-base pieces at symbol starts (PSDBASEX, CLIP2, PSDBASEY, POSITION, GsDRAWENV); the S77 probe's "5 interleaved bases, NOT splittable" grouped by base instead of by run (cookbook §489) |
SYS.o (3109 ins) |
libgpu | .bss+0x150 → 0x800c551c but base recovered 0x80078830; commons scattered 0x80078xxx/0x800c5xxx |
LINKED S78 #4 (libgpu2): .bss [0,0x144) @0x80078830 + .bss2 = _que @0x800C5510 |
MTX_05/07/11,REG03,REG11 |
libgte | sit in libgs gaps gsgap1/2/4/5; gsgap2(48B)≠MTX_07(36B) so the gap stub needs a sub-split | deferred (T11); small GTE fns; link byte-identical, just need the gsgap region resegmented (low priority) |
0x3C438,0x3D424,0x3D94C (S_R/S_GRMDT/S_IH) |
libspu/snd | cross-object commons referenced at a minority address (S_R/S_GRMDT — these objects have NO .bss of their own, so the S78 split does not apply) + a false placement inside SSSTART (S_IH) |
excluded (sound region); stay stubs (24+4+24 ins). VM_F 0x3FA64 (237 ins) left this row in S78 #4 → snd12 |
SSGM.o |
libsnd | isolated @0x1BD80, inside the matched-C region (near func_8001Bxxx) | deferred; 8 ins; would need a 1-object carve amid matched C |
| libapi 800c3 cluster (~22 objs) | libapi | C57..L10/L02/L03 @0x5CE18.. in the 800c3 region (separate from the 800c2 apicard region) | deferred; ~22 4-ins BIOS syscall stubs; lowest value; another region resegmentation |
If scattered- Done in P31 S78 #4 — exactly that fix, twenty-three phases later: .bss proves prevalent across libgte/libspu/libsnd, escalate to a Max general fix (split each object's .bss into per-common NOLOAD sections at their EXE-resolved addresses); otherwise excluding the few affected objects is the GS_001-precedent decision.tools/psyq_bss_split.py runs inside the link-prepare step of psyq_link / psyq_link_region / psyq_integrate and tiles any such section into per-base NOBITS pieces from the bytes (cookbook §489). All three excluded objects link byte-identical; 235 placed objects across the 9 curated dirs, 0 refusals (R39 negative control).
The RESIDENT has no linkable PsyQ footprint — it is custom engine code (Phase 12 T1, byte-verified)
Pre-Phase-12 assumption (now corrected): because the resident detects as PsyQ 4.7.0 (DetectPsyQ + a
DsMix/libsnd signature hit), it was expected to link embedded 4.7 libsnd/libgte/libspu (the Phase-11 close-out + R24 "link the resident's 4.7 libs" opener). The bytes refuted this.
Phase 12 T1 survey ran psyq_identify over the 4.7 objects (tools/psyq/conv47/…/lib/, already ELF — ar x into .run/obj47/<lib>/, no psyq-obj-parser needed) and the 4.0 objects, against the resident (MAIN.CD/FILE_010/1.1, --vram-base 0x800CEDF8), over the code window and the whole file. Result — NIL footprint:
| Lib (4.7) | placed | Lib (4.0) | placed | |
|---|---|---|---|---|
| libsnd | 1/226 (ut_rev_2.o, 4 ins) |
libsnd | 2/163 (VM_DON.o 5 ins, VM_DOFF.o 4 ins) |
|
| libspu | 0/134 | libspu | 0/129 | |
| libgte | 0/509 | libgte | 0/381 | |
| libgpu | 0/61 | |||
| libcd/libmath/libds | 1 each (≤8 ins, two alias the same DsMix addr) |
All hits are the §9.5 short-object coincidental class (≤8 ins, masked patterns match by luck). Tool sanity (the negative is real): 4.0 libsnd vs the EXE snd region = 35/163 placed — the tool works and is version-sensitive; the resident genuinely embeds no stock PsyQ object of either version.
Ghidra corroboration (G1, sampled): DsMix decompiles to { FUN_800d1bf8(); return 1; } — a custom 2-line wrapper that ignores its vol arg (NOT the stock libsnd DsMix; the R13 tag in symbols.resident.txt is refuted). Other sampled functions are game logic (global accessors, engine init calling EXE REAL matches, entity-heading math calling the EXE's libgte RATAN). The resident's code makes 61 distinct EXE-range jal calls (vs 37 internal) — it calls the EXE's resident SDK rather than embedding it.
Conclusion / architecture: the PsyQ SDK lives in the EXE (959 LINKED); the resident is ~143 functions of custom engine code that calls the EXE's SDK + engine via fixed addresses (no RAM-wasting SDK duplication in an always-loaded blob). The DetectPsyQ "4.7.0" was a single coincidental DsMix-region signature, not a linked footprint. Phase 12 matches the resident engine by hand (REAL), not by linking (LINKED stays 0). R24's per-binary-provenance principle holds, but for the resident the practical consequence is "nothing to link." (Regenerate: for L in libsnd libspu libgte libgpu; do d=.run/obj47/$L; mkdir -p $d; (cd $d && ar x ../../tools/psyq/conv47/psyq-4_7-converted/lib/$L.a); python3 tools/psyq_identify.py $d 0x800CEDFC 0x800D3408 --vram-base 0x800CEDF8 --exe extracted/retail/MAIN.CD.dir/FILE_010.dir/1.1; done)
S78 (2026-09-04): the 800c3 "wall" band is LIBPAD 4.2.1 + LIBAPI 4.2, and the LINKED residue is now printed by the build
How it was identified (a free oracle nobody had used). ghidra_psx_ldr ships per-version PsyQ
signature sets — ~/ghidra_12.1_PUBLIC/Ghidra/Extensions/ghidra_psx_ldr/data/psyq/<ver>/<LIB>.LIB.json,
one masked-byte signature + function labels per OBJECT, for 2.6 → 4.7. Matched against the retail EXE
bytes (cookbook §487), the 4.2 set places these byte-exact:
| object | vram | ins | functions (labels) |
|---|---|---|---|
LIBAPI COUNTER.OBJ |
0x8005CF68 | 92 | SetRCnt · GetRCnt · StartRCnt · StopRCnt · ResetRCnt |
LIBAPI C114.OBJ |
0x8005CE48 | 8 | _96_remove — the 4.2 Ps stamp (SETUP §5.1 "libnum 0") |
LIBPAD PADENTRY.OBJ |
0x8005D0D8 | 300 | PadChkVsync · PadStartCom · PadStopCom · PadChkMtap · PadGetState · PadInfoMode · PadInfoAct · PadInfoComb · PadSetActAlign · PadSetMainMode · PadSetAct |
LIBPAD PADMAIN.OBJ (4.2.1) |
0x8005D588 | ~756 | PadEnableCom · _padSetVsyncParam · _padChkVsync · _padStartCom · _padStopCom · _padInitSioMode · _padSioRW (+ _padSioRW2/_padClrIntSio0/_padWaitRXready, order-inferred: the 4.2 sig drifts +4/+12 here — the EXE holds the 4.2.1 revision) |
LIBAPI L02.OBJ/L03.OBJ |
0x8005E168 | 4+4 | SysEnqIntRP · SysDeqIntRP |
LIBPAD PADCMD.OBJ |
0x8005E188 | 600 | _padSetAct · _padSetCmd · _padSendAtLoadInfo · _padRecvAtLoadInfo · _padGetActSize · _padLoadActInfo · _padSetActAlign · _padSetMainMode · _padCmdParaMode |
LIBPAD PADIF.OBJ |
0x8005EAE8 | ~376 | statics only (no public labels): func_8005EAE8/EB28/EC00/ECC0/ED4C |
LIBPAD PADPORTD.OBJ |
0x8005F0C8 | 408 | PadInitDirect |
LIBPAD PADSEQD.OBJ |
0x8005F728 | 288 | _padInitDirSeq · _dirFailAuto |
LIBPAD WAITRC2.OBJ (4.3 sig) |
0x8005FBA8 | 48 | setRC2wait · chkRC2wait |
LIBAPI FIRST.OBJ |
0x80061FA8 | 168 | firstfile (the 4.2 C wrapper; 4.0's A66.o trampoline at 0x80062248 is firstfile2 in 4.2 naming) |
LIBAPI PAD.OBJ |
0x80062388 | 192 | SetInitPadFlag · ReadInitPadFlag · PAD_init · InitPAD · StartPAD · StopPAD |
LIBAPI PATCH.OBJ |
0x800626C8 | 40 | EnablePAD · DisablePAD · _patch_pad |
LIBAPI CHCLRPAD.OBJ |
0x80062768 | 28 | _remove_ChgclrPAD |
The 4.2.1x Ps stamp at 0x80072954 ("libnum 12") stands directly in front of libpad's .data — the
callback tables D_8007295C..D_800729D8 the band's functions index. So all 12 of main's open stubs
in 800c3 (incl. the four §332 "%lo-in-a-delay-slot" walls: _padInitSioMode, _padStartCom,
func_8005ED4C, func_8005F450) and the 8 SDK-C-REORDER verbatims are Sony library code assembled
in reorder mode — §332b's mechanism, with its provenance. Names applied to config/symbols.us.txt +
Ghidra (46; firstfile after retiring Ghidra's 4.0-sig firstfile at 0x80062248 → firstfile2).
Which archives hold it. Placed with psyq_identify after converting each to ELF: PsyQ 4.0
(lib40/, no LIBPAD at all), 4.6 (lib46/, S78 fetch), 4.7 (conv47/): only
PDMAIINI.o (4.6/4.7, 68 ins @0x8005D8B4 = _padStartCom+_padStopCom) is byte-identical in the
band; 4.6/4.7 libpad otherwise differs (PADMAIN 884 ins in 4.4+, PADCMD 768). The loader's 4.3
signature set matches PADENTRY/PADCMD/PADPORTD/WAITRC2 but not PADSEQD (292 vs 288). A 4.2.1 or
4.3 LIBPAD.LIB is the archive that would LINK the whole band (task #13); until then the band is
matched as C under the REORDER_TUS island with the real names.
The LINKED residue, now a build output. psyq_integrate --yaml (S78) wires only the objects
inside each library's stub subsegs and PRINTS the rest as ~~ N located object(s) / M ins OUTSIDE the stub subsegs — the completion contract's "SDK residue empties" line, read straight off make build.
At S78 close it reads, for libgte: 13 objects / 1,264 ins — MSC01/02/05/09 (800b, 276 ins,
100% of that "game code" subseg), SMP_00 (800b_2), SMP_05 (800b_3), FGO_01–06 (800b_5, 804 ins,
100%), PATCHGTE (800b_6, 40 ins, 100%). Plus, outside libgte's window: SYS.o (800c, 3,109),
2D_BG0/1 (800b_7, 1,022), VM_NO1 (sgap_7, 305), VM_NOWON (sgap_8, 300), VM_F (sgap_6, 237),
libapi's 800c3 trampolines (176), FIRST.o (168). Task #3/#4 wire these.
Why the build was RED at HEAD (found S78). The S77 psyq_identify fix (§485) started locating
those in-gap objects, and psyq_integrate.contiguous_blocks() then merged libgte's 22 stub blocks
into 3 (3 object blocks but 22 stubs) — every make build BINARY=main with .run/obj40/libgte
present died at the link. It passed the S77 gates because the gate worktrees have no .run/obj40 and
take the stub fallback. Two fixes, both in psyq_integrate.py: (1) --yaml: stub↔objects by SUBSEG
RANGE with an exact-tiling check, residue printed; (2) a library object's DEFINED symbol whose
recovered address the curated file names differently is --redefine-sym'd to the curated name (R15)
— A66.o firstfile→firstfile2, and it also exposed TOC.o CdGetToc@0x800430B8 mis-curated as
DecDCToutCallback (an xdedup-vs-VS mislabel; libcd 4.0's linked object is the stronger oracle).
S78 task #3 — thirteen "game code" subsegs were library objects: wired as LINKED (exact tiles, 0 tokens)
| new block | was | objects | ins | note |
|---|---|---|---|---|
libgte23 |
800b | MSC01, MSC02, MSC05, MSC09 | 276 | GTE macro wrappers (SetRGBfifo…) that had been hand-matched as REAL |
libgte24 |
800b_2 | SMP_00 | 132 | |
libgte9 (re-derived) |
800b_3 + libgte9 + 800b_4 | SMP_05 (NormalClip) |
12 | SMP_06 NormalClipS is its nested sub-pattern; the "3-nop NOTCODE-PAD" was the object's alignment |
libgte25 |
800b_5 | FGO_01…FGO_06 | 804 | |
libgte26 |
800b_6 | PATCHGTE | 40 | .sbss 0x10, single base |
libgte27/28/29 |
gsgap1/2/4 | MTX_05 / MTX_07 / MTX_11 | 20/12/12 | the Phase-8 "deferred (T11)" libgs-gap objects |
libgte30 |
gsgap5 | REG03 + REG11 | 76 | |
libgs7 |
800b_7 | 2D_BG0 + 2D_BG1 | 1022 | §485's headline find |
snd10 |
sgap_7 | VM_NO1 | 305 | |
snd11 |
sgap_8 (head) | VM_NOWON | 300 | sgap_8 now starts at 0x80040DE8 with its game C |
Result: libgte 70 objects / 30 blocks (was 53/22), libgs 33 / 7, sound 62 / 11; LINKED fns
959 → 1,040; REAL 912 → 886 (26 inline-asm SDK wrappers re-provenanced); VERBATIM 146 → 85;
13 TUs deleted; main 143dbb89 byte-identical with and without the SDK objects (the fallback had
been red since S7x: CdReadyCallback was called by its SDK name but the libcd stub carried
func_800435B4 — curated now). Remaining located-but-unwired: SYS.o 3,109 + VM_F 237 (both
.bss-splittable, task #4), the libpad/libapi band pieces (task #5), SSGM.o 8, and the genuine
scattered-.bss walls GS_001 / S_R / S_GRMDT.
The metric correction this exposed (R35). progress.py's "MAIN game-code weighted" line claimed
its sig excluded the LINKED objects; it never did — the 2026-08-05 Ghidra sig carried all ~31,000
linked-SDK instructions, whose stub records read as unmatched game code. Honest figure, LINKED now
excluded live from the Makefile stub lists + yaml ranges: 91.8% (44,562 / 48,537), not 59.8%; the
3,975-ins remainder equals the sum of main's open stubs in frontier_classify exactly.
S78 task #4 (S79, 2026-09-04) — the scattered-.bss class is closed: SYS.o, VM_F.o and GS_001.o linked via a link-prepare section split
| new block | was | object | ins | .bss pieces (cut → base) |
note |
|---|---|---|---|---|---|
libgpu2 |
800c (56 hand-matched SDK fns + 62 verbatim frags) | SYS.o | 3,109 | .bss [0,0x144) → 0x80078830 · .bss2 = _que → 0x800C5510 |
_que recovered BY NAME from SYS.o's own 4 named refs = 0x800C5510 — the cut is confirmed by an independent oracle |
snd12 |
sgap_6 tail (hand-matched func_8003FA64 = _SsVmFlush) |
VM_F.o | 237 | .bss [0,0x508) → 0x80079580 · .bss2 = _svm_sreg_buf → 0x800B9B58 |
62 other sound objects recover _svm_sreg_buf = 0x800B9B58 |
libgs8 |
gsgap3 (hand-matched as game C: func_800525DC…) |
GS_001.o | 384 | six pieces: 0x80078810 · PSDBASEX 0x800A4F3C · CLIP2 0x800AE820 · PSDBASEY 0x800A4F40 · POSITION 0x800A5E50 · GsDRAWENV 0x800A6438 | the S77 probe called this "5 interleaved bases, NOT splittable" — it grouped by BASE; by RUN it is six symbol-aligned pieces, and the other libgs objects recover all five cut symbols at exactly those addresses |
Mechanism (tools/psyq_bss_split.py, cookbook §489): a pure-Python ELF32 REL rewrite that derives
each reference's base from the game bytes, walks the references in offset order, cuts at symbol starts
between runs, moves the symbols, inserts a LOCAL section symbol per piece, retargets the relocations
with the addend rewritten in the instruction immediates, and self-diffs. It runs inside the shared
link-prepare step (psyq_link.link_object, psyq_link_region.build_region, psyq_integrate.integrate),
derived from the bytes on every build — nothing recorded, nothing to go stale. Negative control (R39):
235 placed objects across the 9 curated dirs, 0 refusals, exactly these 3 splits; an end-of-buffer
reference (.bss + size, libcd) caught the first bounds check and is why problems are fatal only when a
split is actually needed.
Wiring: LIBGPU_ELF is the raw .run/obj40/libgpu (the libgpu_used dir existed only to exclude
SYS.o — retired); libgs 34 objects / 8 blocks (make_libgs.sh +GS_001); sound 63 / 12 (make_snd_used.py
exclusions 4 → 3). Three TUs went: src/800c.c, src/gsgap3.c, and the _SsVmFlush body of
src/sgap_6.c (its 4-ins game function stays). Main 143dbb89 byte-identical WITH and WITHOUT the SDK
objects (fallback from a fresh extract). Remaining located-but-unwired SDK code in main: the libpad/libapi
band pieces (task #5), SSGM.o 8 ins, and the two cross-object-common walls S_R/S_W + S_GRMDT*
(no .bss of their own — a different class from this one).