30 KiB
PsyQ library integration worklist (Phase 8)
COMPLETE at P31 S79 #5 (2026-09-04). Every library row below reads DONE; main's 1,256 LINKED functions are Sony's objects, byte-identical by link (PhaseEnd_Phase31/32). The only residue is named at the tail of this file:
SSGM.o(8 instructions inside matched C) and the sound region's two cross-object-common walls — located, not wired, and not stubs. Kept as the per-object record of what was linked (banner added 2026-09-07, Phase 33.5).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 | 26 (800c2) + 23 (band) | 800c2 (0x80061F38–0x80062888) + the band (0x8005CE18–0x8005E188) |
~2.6k | ✅ DONE (S79 #5): apicard region 26 objs / 7 blocks tiling the whole range (tools/make_apicard_used.py, libapi 4.2 from tools/psyq/lib421 + libcard 4.0; FIRST/PAD/PATCH/CHCLRPAD were the "game code" rows 800c2/800c2_2/800c2_3) + the band's 23 libapi 4.2 objects (libapi1/libapi2) from the raw .run/obj42/libapi42 dir, windowed. |
| 9 | libpad 4.2.1 | 7 / 11 | the band (0x8005D0D8–0x8005FC68) |
~11.5k | ✅ DONE (S79 #13 found it, #5 wired it): PADENTRY PADMAIN PADCMD PADIF PADPORTD PADSEQD WAITRC2 as libpad1/libpad2 from the raw .run/obj42/libpad421 dir (SCE's 1998-02-26 J421PD patch, tools/psyq/lib421). Absent: PADGUN/PADPORTM/PADSEQM/GUNHOOK (no gun, no multitap). |
| 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) | LINKED S79 #5 (libapi1/libapi2, from libapi 4.2 — the EXE's real libapi, cookbook §490); the row's "lowest value" verdict missed that the same region held libpad 4.2.1's 2,780 ins |
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).
S79 task #13 — FOUND: the archive that links the whole band (libpad 4.2.1 + libapi 4.2), 46/46 byte-identical
Where. archive.org item play-station-programmer-tool-runtime-library-version-4.2.7z (383 KB; sha256 in
tools/psyq/CHECKSUMS.sha256; now tracked as tools/psyq/PlayStation_Programmer_Tool_-_Runtime_Library_Version_4.2.7z,
R20). It is the PsyQ Runtime Library 4.2 (LIB/*.LIB + INCLUDE, dated 1998-01-21) plus LIB/42PATCH/J421PD.ZIP
— SCE R&D's "Libpad.lib version 4.2.1 for the Analog Controller (DUAL SHOCK)" notice of 1998-02-26, which
ships LIBPAD.LIB 4.2.1 together with LIBAPI.LIB 4.2 and the matching LIBPAD.H/LIBAPI.H/KERNEL.H (the
prototype oracle for the band from now on — not the 4.7 headers). Extracted to tools/psyq/lib42/ and
tools/psyq/lib421/ (gitignored, regenerable); ELF in .run/obj42/{libpad421,libapi42}.
Result (the success test from the S78 brief, passed). psyq_identify over 0x8005CE18–0x800629DC, then
psyq_link.py per object — every located object links byte-identical:
| library | placed | objects (vram, ins) |
|---|---|---|
| libpad 4.2.1 | 7 / 11 | PADENTRY 0x8005D0D8 (300) · PADMAIN 0x8005D588 (760 — the 4.2.1 build, exact) · PADCMD 0x8005E188 (600) · PADIF 0x8005EAE8 (376) · PADPORTD 0x8005F0C8 (408) · PADSEQD 0x8005F728 (288) · WAITRC2 0x8005FBA8 (48); absent: PADGUN, PADPORTM, PADSEQM, GUNHOOK (the game uses neither multitap nor gun) |
| libapi 4.2 | 39 / 88 | the 21 band trampolines C57 C68 C73 C114 A07–A13 A23–A25 A36 A37 A52 A53 A91 L10 (0x8005CE18–0x8005CF58) · COUNTER 0x8005CF68 (92) · L02/L03 0x8005E168 · and in the apicard region C112 A50 A51 A54 A65 A67 A69 (0x80061F38–) · FIRST 0x80061FA8 (168, the C firstfile — REAL C today) · A66 0x80062248 · PAD 0x80062388 (192) · A18–A21 0x80062688 · PATCH 0x800626C8 (40) · CHCLRPAD 0x80062768 (28) |
libpad 4.2 (plain, LIB/LIBPAD.LIB) |
4 / 11 | PADENTRY PADCMD PADPORTD PADSEQD only — PADMAIN/PADIF/WAITRC2 are the 4.2.1 fixes; this is what the loader's "4.2" signature set was generated from and why PADMAIN showed +4/+12 drift in S78 |
.data anchors agree with S78: PADMAIN's .data is at 0x80072954 — the 4.2.1x Ps stamp stands at its
head; COUNTER .data 0x80072934, PADIF 0x800729D4, PADPORTD 0x800729F4, PAD 0x80072A24; PADENTRY .rdata
0x800744A0.
What this changes for task #5. The band is no longer "C under the reorder island with real names": ALL of
0x8005CE18–0x8005FC68 (2,872 ins: 21 trampolines + COUNTER + libpad's seven + L02/L03) is linkable from real
objects — the 12 open band stubs, the 8 SDK-C-REORDER verbatims and the band's hand-matched C are all Sony
code and become LINKED. The apicard region's libapi pieces (currently 4.0 objects in apicard_used, byte-identical
twins) and FIRST.o (currently REAL C firstfile) come from the same 4.2 library — the EXE's actual libapi
(the 4.2 Ps stamp, SETUP §5.1 libnum 0). #5 = carve 800c3 / 800c2* at object boundaries (§486/§488
procedure) + a curated .run/obj42 dir + two integrate calls. The REORDER_TUS island (§332b) then holds no
game code at all and can be retired with it.
The 4.3 disc for the record. Programmer Tool - Runtime Library Version 4.3 (Japan)_DTL-S2340_redump.zip
(archive.org item ps1_sdks, 403 MB; disc dated 1998-05-18) was fetched for comparison only: its PSX/LIB/LIBPAD.LIB
places the same 4 objects as plain 4.2 (PADENTRY, PADCMD, PADPORTD, WAITRC2) — PADMAIN is 832 ins, PADIF 380,
PADSEQD 292 against the EXE's 760 / 376 / 288 — and its LIBAPI.LIB places 38 (no 4.2 C114). So 4.2.1 is the
unique exact match: the game was built between the February 1998 patch and the May 1998 4.3 disc. Not banked
(>100 MB, R20 exception; the item id is the pointer; the two LIBs sit in .run/psyq_hunt/rtl43/lib43/).
S79 task #5 — the band and the apicard region LINKED from libapi 4.2 + libpad 4.2.1 (0 tokens; cookbook §490)
| new block | was | objects | ins | note |
|---|---|---|---|---|
libapi1 |
800c3 head | C57 C68 C73 C114 A07–A13 A23–A25 A36 A37 A52 A53 A91 L10 + COUNTER | 176 | 21 BIOS trampolines + the RCnt object; window 0x8005CE18–0x8005E188 |
libpad1 |
800c3 | PADENTRY + PADMAIN | 1,060 | PADMAIN 760 = the 4.2.1 build |
libapi2 |
800c3 | L02 + L03 | 8 | SysEnqIntRP / SysDeqIntRP |
libpad2 |
800c3 tail | PADCMD PADIF PADPORTD PADSEQD WAITRC2 | 1,720 | window 0x8005D0D8–0x8005FC68 |
apicard5 |
800c2 | FIRST | 168 | firstfile (REAL C until now) + the "no jump table wall" stub func_80062144 |
apicard6 |
800c2_2 | PAD | 192 | SetInitPadFlag … StopPAD + 4 statics (hand-matched C + 4 verbatims until now) |
apicard7 |
800c2_3 | PATCH + CHCLRPAD | 68 | _patch_pad, _remove_ChgclrPAD (4 verbatims until now) |
Two windowed integrate calls from the raw .run/obj42/{libapi42,libpad421} dirs (the band is one interleaved
run and psyq_integrate tiles each stub with one library) + the apicard call from the re-sourced
.run/obj42/apicard_used (libapi 4.2 + libcard 4.0, 26 objects / 7 blocks, no game code left in
0x80061F38–0x80062888). TUs gone: src/800c3.c (129 hand-matched "C", 62 verbatim bodies, 19 stubs — Sony's
reorder-assembled code all along, §332b), src/800c2.c, src/800c2_2.c, src/800c2_3.c; the REORDER_TUS
island is EMPTY (the Makefile mechanism stays). main 143dbb89 with every SDK dir and, from a fresh extract,
with none. config/wave_exclude.txt regenerated (exclude_audit now lets LINKED outrank a pinned WALL: the
four §332 walls, func_80062144, and PopMatrix/PushMatrix — in libgte3 since Phase 8 — dropped);
verbatim manifest 33 → 6 rows. Located-but-unwired SDK code left in main: SSGM.o (8 ins, inside matched C)
and the sound region's two cross-object-common walls (S_R/S_W, S_GRMDT*).