D196 - fpPS4 does not crash on an unimplemented import - it sleeps forever, and that is one line
The question was whether fpPS4's stalling was ours or theirs. It is theirs, it is deliberate, and the code says so plainly:
procedure print_stub(nid:QWORD;lib:PLIBRARY); MS_ABI_Default;
begin
Writeln(StdErr,SysLogPrefix,'nop nid:',lib^.strName,...);
//DebugBreak;
Sleep(INFINITE);
//readln;
end;Every unresolved function import is bound to this
(Stub.NewNopStub(...,@print_stub)). The commented-out
DebugBreak and readln beside it show the
intent: freeze the process so a developer can attach and identify the
missing function. Reasonable for that workflow. For an unattended sweep
it means one missing function costs the whole run
silently - the process stays alive and stops producing
output, which is indistinguishable from a hang in the guest, and is
exactly what "fpPS4 seems stuck on 9/27" was.
Directly above it sits _nop_stub -
xor %rax,%rax; ret - the returning alternative, unused. The
compiler says so on every build: "Local proc _nop_stub
is not used".
Nothing on our side could have avoided it
obSCEne declared strspn, imported it, and announced it
before calling. A call that never returns cannot be detected by its
caller; announce-before-attempting is the mitigation,
and it worked - the dangling try named strspn
exactly. There is no defensive change available to the probe, which is
worth stating because "could we be more careful" was the open
question.
The patch, and why it returns a value rather than nothing
patches/fpps4-probe-friendly.patch.
print_stub becomes a function returning QWORD
zero.
Not a bare procedure, because the trampoline is
push rdx; push rcx; ...; call rax; pop; pop; ret - it hands
the guest whatever the handler left in rax. Undefined there
is not harmless: a pointer-returning function given garbage crashes at
the first dereference, far from the cause. This is the same mistake that
crashed Kyty at entry when its trampoline returned
0x7FFFFFFF.
What it bought
| stock | patched | |
|---|---|---|
| records | 36 | 36,631 |
| outcome | hung in 007-responsive/libc |
complete, 27/27 sections, 515/515 checks |
| wall clock | ran until killed | 2s |
| unimplemented imports survived | 0 | 181 |
181 is the number that settles "is fpPS4 worth continuing with". At two runs per blocker under D191, converging the stock build would have taken roughly 362 runs. Patched, it takes one, and fpPS4 is now the fastest loader in the sweep.
It also makes the measurement correct rather than merely
available: a stub that returns is reported by
007-responsive as silent, which is the true
answer - the same verdict prosper's registry independently confirmed 54
times over. (D195)
Where the row goes
reports/fpps4-patched.txt, not the stock row. The
behaviour was changed, so the result is about a build only this machine
has. (D176)
Status: derived - read from their source, measured against both builds.