D520 - Starting every placed module before entry is not the missing ordering

measured - 2026-09-03 (three interleaved pairs, identical each time)

D519 established that the wall is a call ordering: the guest reaches code needing its memory manager before the line that creates it. D515 left one ordering question open in those words:

Whether the platform runs a placed module's initialisers at load time for modules bound as imports is unmeasured, and answering it by starting everything would be inventing behaviour.

libc.prx is exactly that case - placed, carrying one initialiser, and never started, because nothing ever loads it by name. So the question is worth an experiment rather than an argument.

The instrument

ORBISTOUN_START_MODULES runs every placed module's DT_INIT and DT_INIT_ARRAY after protection and after the guest float environment is adopted, which is the last point before the guest's own entry. Off by default, Intervenes, and it reports per module.

The answer is no, and it is emphatic

off   181 distinct, 415,335 calls, read of 0xa0 at image+0x1389269
on     15 distinct,     271 calls, read of 0x7fff0001

Three interleaved pairs, identical every time. Starting everything does not move the wall forward; it destroys the run, and it fails in a way that says why: read of 0x7fff0001 is orbistoun's own placeholder error code being used as an address, so a constructor called something nothing implements and wrote through the answer.

Il2CppUserAssemblies and PS5Util start and their constructors do real work - _Znwm, pthread_key_create, pthread_mutexattr_init, __cxa_atexit - and then the third faults.

So the missing ordering, whatever it is, is not "initialise everything before main". A whole class of explanation is eliminated rather than left arguable, which is what the diagnostic was for.

And the instrument failed the way it was built to catch

The first version printed what it had done after doing it. A constructor is guest code and can fault, and one did - inside the third module - so the loop never returned and the line never printed. The intervention read as though it had not run, which is exactly the ambiguity D325 exists to remove, reproduced in a new diagnostic on its first use.

It now says what it is about to do before doing it, and what each module did is recorded as it goes and reported from persist, which every ending reaches (D513). The evidence that it ran survived only because that recording already existed.

A handle nobody asked for is not reported as a handle

A module started this way has no handle, because no guest called sceKernelLoadStartModule for it. Reporting one - even 0xffff_ffff_ffff_ffff - puts a number in front of a reader that a guest could be thought to hold. It reads before entry instead, and the test breaks on exactly that wording:

BREAK: invent a handle
  -> ARecordedModule (0 initialiser(s), handle 0xffffffffffffffff)   FAILED

What this does not establish

That the console does not initialise import-bound modules early. It establishes that orbistoun cannot do it here, this way, at this point without breaking a run that otherwise gets 415,335 calls further. The console has a working libc before a title's constructors run; orbistoun does not, and that difference is a better explanation of this result than anything about the console's loader.

So the question D515 asked is still open. What is closed is the shortcut answer to it.