The settled half of the C library, promoted from counting to checking

Thirty names moved out of the census and eleven behavioural checks written against them, all OBS_FROM_SPEC (D051). Six in 037-math - round, trunc, exponentials and logarithms, the remaining inverse trigonometry, the whole single-precision family, and text-to-float - and five in 035-libc: wide integer text, wide absolute value, bounded case comparison, strdup, sprintf.

Every one aims at a plausible wrong answer rather than the happy path. round(-2.5) separates away-from-zero from nearest-even and from "add a half and truncate", which disagree with each other and with the standard. trunc(-2.7) catches a forward to floor. llabs(-4294967296) catches a forward to abs. strtoull("010", 0, 0) catches a base-zero that does not read the prefix. strdup is checked by writing to the copy, because one sharing storage with a string literal compares equal and faults later and somewhere else.

All eleven pass under make host against glibc, which is the step that exists because a probe with a wrong expectation reports a working implementation as broken. The fmod(7, 4) and fmod(11, 4) embarrassment is recent enough to still be steering the work: the new fmodf check uses 9 and 4.

Two surprises, one of them old

div and ldiv were written, passed, and then withdrawn (D052). The struct-by-value return is an ABI path nothing else here touches and it looked worth having. ISO C does not specify the member order, so the check read the pair as an unordered set - honest, and it said what it could not tell. What killed it was the host build: reaching the real div_t means including <stdlib.h>, glibc defines several of platform.h's other declarations inline, and one conflict became a dozen. Keeping the check meant giving up running it under make host, and that is the wrong trade - a check that has never seen a known-good implementation is not evidence. Back to the census, with the reasoning written into platform.h where the next person to have this idea will find it.

The census had been carrying X(FromAddr) (D053), and had been for some time. It is the tail of sceKernelGetModuleInfoFromAddr, decapitated when sceKernelGetModuleInfo was promoted out of the census by a match that ignored word boundaries.

It cost twice. FromAddr is not a symbol so it censused as absent forever, which the header is right to call harmless. sceKernelGetModuleInfoFromAddr is a symbol, and it silently stopped being counted while the list still appeared to contain it. Nothing would have caught this: the build succeeds, the census runs, the report is well-formed. It was found by reading the generated header for an unrelated reason.

gen-surface.py now refuses any name not shaped like a symbol - sce and a capital, two leading underscores, or a lowercase first letter. Crude on purpose: it costs nothing and it closes the only route this has.

Found, notably, by a scan written to check that this session's removals had not done the same thing. They had not. The previous one had.

State

verify.sh green: 86 tool tests, lints clean, three targets, derivation agrees, payload refused. 404 imports, none unplaced. Census is 260 symbols, down from 290 and up one real name.

Not yet run on an emulator. The shadPS4 install used for every previous run was in the scratchpad and is gone, so these eleven checks have been validated against glibc and not against the thing they were written to measure.