D442 - The flexible-memory "shared allocator" theory was aimed at the wrong titles

measured - 2026-09-01 (user-directed, /loop)

The standing theory (carried in the summary going into this session) was that PPSA25872's and PPSA02664/03416's C++ allocators fail because they size themselves off sceKernelAvailableFlexibleMemorySize, which orbistoun answers from the ~5.25 GiB direct pool instead of a game's real flexible budget. A static import survey of all four resident titles retires it:

title flexible-memory imports
PPSA02664 none - only sceKernelGetDirectMemorySize
PPSA25872 none - only sceKernelGetDirectMemorySize
PPSA03416 none - only sceKernelGetDirectMemorySize
PPSA21564 sceKernelConfiguredFlexibleMemorySize and sceKernelAvailableFlexibleMemorySize

Three of the four never call a flexible-memory function at all. Their allocators size off sceKernelGetDirectMemorySize, which orbistoun already answers correctly with the measured 0x1_4000_0000 (D398). So there is no flexible-memory bug for them to fix, and the interim decision to keep available_flexible_memory_size reading the pool (D273, and the comment on [FLEXIBLE_MEMORY_SIZE]) costs those three nothing.

The one title that does use flexible memory names the real gap: sceKernelConfiguredFlexibleMemorySize is unimplemented. PPSA21564 imports it (the configured total, distinct from available = configured minus mapped; the vendor libkernel exports both, at 0x1a9c0 and 0x1a920). An unimplemented import resolves to the Unimplemented placeholder, so if the title reaches that call it is told nothing.

Why this is not yet an implementation. The honest value of configured/available for a game is that game's own budget, and a game declares it in its PT_SCE_PROCPARAM segment via the kernel mem-param - not something transferable from obSCEne's homebrew measurement (per-process, the D273 argument) and not a constant orbistoun may invent (principle 3). Reading that structure is a binary-format concern, which is SELFish's remit, not orbistoun's - the proc-param/mem-param layout belongs in selfish-elf beside the dynamic/dynlib tables it already reads, sourced from a citable open-source implementation. It is deferred here rather than half-built in the wrong crate:

Recorded so the next session does not re-chase the three-title theory this one falsified.

Update, same day - the proc-param reader was built, and it falsified this entry's own second premise.

This entry deferred the proc-param reader to SELFish on the belief that a game declares its flexible budget in its mem-param. That belief was untested, and testing it needed the reader - so it was built, as a reading-half in orbistoun-elf beside the dynamic/reloc tables that already live there (orbistoun-elf::procparam, SCE_PROCPARAM, Container::proc_param_bytes; surfaced through ProcParamInfo and orbistoun-cli inspect). This partially reverses the "defer, do not half-build" stance above, on four grounds: the layout is citable and hardware-confirmed (obSCEne's crt.c, which launches on real hardware, cites the OpenOrbis PS4 ELF specification and pins the mem-param pointer to +0x40 from the D219 fault); orbistoun already owns its ELF readers while SELFish is mid-rescaffold and unbuildable; the reader changes no runtime behaviour (it is inspection only); and without it this entry's deferral rested on an assumption nobody had checked. It should migrate to selfish-elf once that repository is back, like the other reading halves eventually will.

The check the reader made possible - and it is decisive. The three param pointers are stored as RELATIVE relocations (zero in the file, base + addend at load), so they must be resolved through the data relocation table, not read raw. Resolved, every resident title's mem-param block is present but empty past its size field - and obSCEne's own eboot, whose crt.c demonstrably fills those pointers, resolves the same way, which is what confirms the resolution is correct rather than reading zeros:

title mem-param vaddr size contents past size
PPSA21564 0x88dc0e8 0x40 all zero
PPSA02664 / 03416 0x19240a8 0x38 all zero
PPSA25872 0x1fe80a8 0x40 all zero
PPSA04263 0x3b10138 0x40 all zero
PPSA28061 0x1a80c8 0x40 all zero
obSCEne (control) 0x730378 0x38 all zero

So no title on disk declares a flexible-memory budget. The mem-param-override path this entry pointed SELFish at does not exist in any of them; every title launches under the default budget.

Which corrects D273 and the FLEXIBLE_MEMORY_SIZE comment. Both say obSCEne's measured 0x1b40_0000 is a per-process figure that must not transfer because "a game reserves far more" through its own budget. The evidence above is that no game reserves more - every one carries the same empty mem-param obSCEne does, so obSCEne measured sceKernelAvailableFlexibleMemorySize under exactly the condition every title launches under. 0x1b40_0000 is therefore the system-default available figure and system-wide, the kind that transfers. (obSCEne's flexible round-trip also maps at ~0x2_0037_4000, a region well clear of the direct pool, confirming flexible memory is a separate space, not a view of the direct pool as D273 models it.)

Still not wired, for narrower and now-precise reasons:

The reader and its inspect output are the durable product of this turn: SDK-version detection for free, and the oracle any future flexible-memory work confirms a cited mem-param layout against.

Follow-up: the obSCEne probe named above now exists. obSCEne D283 adds 020-memory/flexible-configured, reading sceKernelConfiguredFlexibleMemorySize and reporting the value - the configured default this entry said was unmeasured. Once a hardware run captures it, orbistoun can implement sceKernelConfiguredFlexibleMemorySize against a measured figure (system-wide, since no title overrides it) rather than the Unimplemented placeholder, and seed the separate flexible allocator scoped above. Still gated on a title reaching the call.