D348 - klogsrv prints its own banner
decided ยท 2026-08-27
D343 got
two payloads as far as calling getopt. Six functions later
one of them runs:
.----------------------------------------------------------------------.
| _ _ _ __ |
| | | __ | | ___ __ _ ___ _ __ __ __ ___ | | / _| |
...
| v0.9 Copyright (C) 2025 John Tornblom |
'----------------------------------------------------------------------'
main.c:278:sysctl: error 0 (orbistoun has no message table)
That is the guest's own output, and the last line is the
guest naming its own next blocker - by file and line, through
this project's own strerror text. CLAUDE.md
calls printf "the cheapest oracle in the project"
for exactly this reason; here the whole diagnostic path had to exist
before the program could say anything at all.
klogsrv went from 3 imports and 3 calls to 9 and 18; ftpsrv from 1 to 8.
What was built, and the one shape they share
| function | why it was next |
|---|---|
signal |
first instruction after main - SIGPIPE, so
a write to a closed socket does not kill the server |
getopt |
second - a server that cannot parse its arguments never opens a socket |
__error |
errno is *__error(), so a placeholder
becomes a wild pointer the guest reads |
strerror |
fed straight into printf unchecked - a placeholder
faults inside the print |
fprintf |
how the error path speaks |
puts |
eight calls of banner, ignored |
Three of the six - getopt, __error,
strerror - cannot be implemented as a return
value. They report through storage: the guest's own
optind, a per-thread errno, a message buffer.
D307 predicted this was coming ("the first time the HLE layer would
own state rather than functions") and D323 built half of it; this
is the other half.
orbistoun-thunk::data_symbol closes it.
DataBlocks already reserved a page per data import; it now
keeps the names too, and publishes them before entry.
An implementation that must leave something where the guest will read it
can find the guest's own slot. A guest that never imported
optarg gets no write rather than an invented one.
Entering at
main needed arguments to match
main is main(int argc, char **argv), and
EntryArgument's options all answer a different question -
what a process entry point finds in its first register. Handing
that to main gives it a wild count to iterate over wild
pointers, which getopt then has to refuse.
EntryArgument::MainArguments reads argc and
argv out of the process image already written to the guest
stack. Read rather than constructed: a second copy
could disagree with the one everything else in the guest will find.
enter_guest_with_argument grew a sibling taking two,
rather than a second copy of forty lines of inline assembly -
rsi was always being set, to nothing in particular.
Six knowledge records, because the tree refuses without them
every_implemented_function_is_written_down failed the
moment signal was bound: "signal is implemented but
nothing is recorded about it". That is principle 1's accounting
working exactly as designed, and it caught all six in one go.
Five are published - POSIX and FreeBSD are the citable
reference. strerror is
assumed, and deliberately: its message is ours,
says so, and does not imitate a table nobody here has seen. A guest
comparing it against a known string would fail; no caller measured does,
they print it.
What is next, and the guest said it
sysctl, at main.c:278. Then
getpid. Then the sockets that are the actual point.