D482 - A title's own module is found by the name that imports it, not by a path
measured - 2026-09-03 (user-directed plan, the TitleOwn loader)
PPSA02664 imports four symbols from a library called
Il2CppUserAssemblies. Nothing answers them, and that is the
title's wall. The module that answers them - all four NIDs, confirmed -
is Media/Modules/Il2CppUserAssemblies.prx, sitting in the
title's own data.
The loader has to find it. This decides how.
What the executable actually says, which is less than was assumed
The vendor tables an encoded import name indexes hold bare names:
libraries (38), indexed by an import's library id:
0 Il2CppUserAssemblies
1 PS5Util
modules (36), indexed by an import's module id:
1 Il2CppUserAssemblies
2 PS5Util
No path, in either table, and DT_NEEDED is a third list
that is not what those ids index (D117). The executable names a
module and says nothing about where it lives. So a loader
cannot follow a reference; it has to search.
Where to search, and
why not Media/Modules/
Three titles in the corpus ship this module. All three put it under
Media/Modules/ - so it is a convention of whatever built
them, and it is not a platform path: the platform's own
directories are three fixed ones holding 537 modules, and this is not
among them (worklog 328).
A convention is not a guarantee, and hardcoding one is how a loader
works for three titles and silently fails for the fourth. So:
search the title's own tree for a file whose stem is the library
name, with a .prx or .sprx
extension.
That rule is measured rather than guessed, and the corpus makes the point sharply:
| title | imports from | file on disk |
|---|---|---|
| PPSA02664 | Il2CppUserAssemblies |
Il2CppUserAssemblies.prx |
| PPSA03416 | Il2CppUserAssemblies |
Il2CppUserAssemblies.prx |
| PPSA25872 | Il2cppUserAssemblies |
Il2cppUserAssemblies.prx |
The filename is the library name, case included. The
third title spells it with a lower-case c in both places.
That is what makes an exact match the right rule and a clever one
wrong.
The case question, which turned out not to exist
This was carried for two ticks as "the executable imports 8 from
Il2CppUserAssemblies and 4 from
Il2cppUserAssemblies, so matching must handle both
spellings". It does not. Those were corpus-wide counts
across three executables; each one is internally consistent, and the
variant belongs to a different title entirely. There is no within-module
ambiguity to resolve.
Matching is still done case-insensitively, but for a smaller and honest reason: the host filesystem may not preserve case, and a title whose file and import genuinely disagreed would be a fact worth discovering rather than a crash. Where several files match, the exact-case one wins.
Order: place everything, then relocate everything
A title module may import from another of the title's own. Placing and relocating each in turn makes the answer depend on which was found first, which is a filesystem-order dependency and the kind of bug that reproduces on one machine.
So two passes: place every module first, collect every export, then relocate. By the time anything is bound, every address that could be bound to exists. The main executable is relocated last, for the same reason and because it is the one whose imports this exists to answer.
What is deliberately left open
Whether the console binds these lazily. A platform loader given a bare name and no path might resolve at first call rather than at load, in which case the title would load its own modules explicitly and binding would follow. Nothing here establishes which, and it does not change this decision: orbistoun binds eagerly at relocation, so it needs the exports available before the main executable is relocated either way.
Recorded because the two are distinguishable on hardware and would matter the day a title loads a module after start and expects an unbound import to begin working. That is a different mechanism, and this decision does not provide it.