D101 - A module built here cannot carry a weak undefined import, by construction; the forcing to GLOBAL stays

Status: decided. 2026-09-10.

mkmodule forces STB_GLOBAL on every resolved import and refuses an unclaimed one, so a module built through this toolchain cannot carry a weak undefined import. That is kept. The consequence Orbistoun identified - a weak-symbol absence control cannot survive packaging - is real and is a property of the toolchain, not an accident of a sample.

The question

Orbistoun (REQ-20260910T1105Z-5d20) asked whether mkmodule should preserve STB_WEAK for an import whose source carried it, rather than forcing GLOBAL on all of them. The tension is genuine, between two measured facts:

Orbistoun framed it as (a) the platform's packager strips weak binding, so all-GLOBAL is faithful; or (b) the titles measured simply had no weak imports and forcing GLOBAL over-generalises. The discriminator it proposed: does a genuine title ever emit a STB_WEAK dynamic import?

Why (b) is refuted, without needing a retail title

(b) assumes preserving source binding would be harmless. It would not. obSCEne's probe declares every platform import weak, so that an absent one links as null rather than failing the link - the dynamic symbol table it hands mkmodule is all-WEAK undefined. And obscene#D248 measured what the real loader does with that: a module whose 203 imports were all WEAK FUNC had them bound only from the two libraries already resident (libkernel, libSceLibcInternal); every other declared library was mapped and left unbound, and fourteen imports stayed null whose symbols the same process could resolve by name moments later.

So preserving source binding would produce a module whose real imports do not bind - which is the exact defect the forcing was introduced to fix. Forcing GLOBAL is load-bearing and measured, and it stays. This holds whatever retail titles do, so the retail-title measurement Orbistoun asked for is not needed to decide it. (I could not supply it regardless: there is no genuine title in this repository to read, only obSCEne's own output.)

Why "all-GLOBAL" is structural, not a happenstance of the sample

A weak undefined import cannot reach a finished module through this toolchain, for reasons upstream of any sample:

obs_census_control_absent is in that manifest (src/probe/imports.c:406, under libSceLibcInternal), so mkmodule hashes it to a NID and marks it GLOBAL like every other import. Measured: the packaged PPSA99980/eboot.bin carries 244 imports, all resolved to libraries (96 libkernel, 86 libSceLibcInternal, ...). There is no path by which it could have stayed weak: leaving it out of the manifest fails both builds; putting it in forces GLOBAL.

So Orbistoun's conclusion is correct and its (a) branch is the right resolution - the packaged census cannot feature-detect by weak symbol - but the mechanism is not a packager stripping a binding. It is the loader refusing to bind weak imports (D248) plus two independent refuse-unclaimed invariants. A weak undefined import is not stripped here; it is never representable here.

The one thing nobody has measured, which is the real hinge

What makes the payload-leg control read "absent" is the gABI rule for an unresolved weak symbol: bind to zero. In the packaged leg the symbol is a GLOBAL import carrying a NID that libSceLibcInternal does not export. What a real loader does with that - leave it null (absent), or fault the load - is unmeasured. orbistoun's emulator stubs it (present), which is orbistoun's choice and may or may not match hardware.

That is the fact that actually governs whether the packaged leg could ever be a meaningful control, and it is a hardware question - obSCEne's to answer, not settleable here. This decision does not depend on it: the forcing stays regardless, because it is demanded by the real imports, not by the control.

Consequence, recorded for both sides

No code change: the behaviour is correct and already pinned by the two tests cited above. This entry is the record of why it cannot be relaxed for a symbol that was meant to be absent.