# `source/networking/telnet_console.obj` — reconstruction **Status: CLOSED — all 17 of 17 symbols byte-exact under the strict comparator (5/5 functions, 1344/1344 `.text` bytes, all 11 string literals, `.bss`). Was 3/5 functions, 624/1344 bytes when §5 below was written.** **Lane:** `claude/libcmt-stream` · **Object status:** `Matching` **Toolchain:** XDK 3911 `CL.Exe` 13.00.9254.1 · **Flags:** the game set, `/O2 /Oy- /DDEBUG /Dxbox` **Start state:** the source was a pure skeleton — symbol-map header and empty section comments, no code. Every symbol was missing. | symbol | target | ours | exact | |---|--:|--:|:--:| | `_telnet_console_dispose` | 80 B / 7 rel | 80 B / 7 rel | **yes** | | `_telnet_console_print` | 192 B / 18 rel | 192 B / 18 rel | **yes** | | `_telnet_console_process` | 352 B / 33 rel | 352 B / 33 rel | **yes** | | `_bss_00456d00` | 140 B / 0 rel | 140 B / 0 rel | **yes** | | all 11 string literals | — | — | **yes** | | `_telnet_console_initialize` | 224 B / 18 rel | 224 B / 18 rel | **yes** (§6) | | `_code_001203c0` | 496 B / 30 rel | 496 B / 30 rel | **yes** (§6) | --- ## 1. The globals block Byte-exact, which confirms the layout. Recovered by reading the **relocation addends** — every reference into the block resolves to one of four offsets (0x0, 0x4, 0x8, 0x88): ```c struct telnet_client { struct transport_endpoint *endpoint; /* +0x04 */ char buffer[128]; /* +0x08 */ }; struct telnet_console_globals { struct transport_endpoint *listening_endpoint; /* +0x00 */ struct telnet_client clients[1]; /* +0x04 */ boolean initialized; /* +0x88 */ }; ``` 4 + 132 + 4 = 140. **`MAXIMUM_TELNET_CLIENTS` is 1**, proven by the scan loop in `telnet_console_process`: it strides `add eax,0x84` (132 = one client) from `globals+4` with the bound `globals+0x88`. Defined `= {0}` so the symbol lands in `.bss` owned by this TU rather than becoming COMMON — the `thread_win32` precedent. ## 2. Facts recovered from the disassembly * **Port 23** (`mov word ptr [ebp-6],0x17`), `address_length` 4. * **`create_transport_endpoint(0x12)`** — the endpoint type constant. * **`_code_001203c0` takes its client pointer in ESI.** VC7 gives statics with fully-known call sites a custom calling convention; the call site does `push count; push buffer; mov esi, OFFSET globals+4; call`. Written as an ordinary third parameter, the compiler reproduces this on its own. * **The character switch** uses a byte index table at `+0x1e0` and a 4-entry jump table at `+0x1d0`. Decoding both gives the cases exactly: `c=4` disconnect, `c=8` backspace, `c=10`/`c=13` execute via `hs_compile_and_evaluate`, other values in 4..13 skip. Printable (`isalnum`/`ispunct`/space) appends. * **`read_endpoint` into a 32-byte stack buffer**, with the returned count `push`ed *before* the branch so it serves as the argument to either `code_001203c0` or `transport_error_to_string`. ## 3. Source-shape findings Five real corrections, each verified by the gate: 1. **`print` tests its write result once, at a shared join.** January writes `"\r\n"`, then the string, then optionally the pending input buffer, and tests **once** at a join reached both by fall-through and by the empty-buffer branch. A short-circuit `a>0 && b>0 && (!buf[0] || c>0)` adds a branch and shifts every later offset. Using a `result` local and testing at the join made it exact. 2. **`process` indexes the globals directly, it does not cache a `client` pointer.** The three missing relocations were all references to `bss_00456d00+0x4/+0x8/+0x88`. Rewriting the second half to use `bss_00456d00.clients[0]` throughout recovered them. 3. **`process`'s greeting branch is written failure-first.** January's layout is `jg ` falling through into the delete; writing `if (write>0) {assign} else {delete}` produces the opposite layout. Inverting to `if (write<=0) {delete} else {assign}` made the function exact. 4. **`initialize` uses `if (endpoint) {...} else {error}`, not an early return.** The positive form places all three error blocks out of line at the end, in reverse order, with the bind and listen paths tail-merging into a shared `delete` + NULL + return (`jmp 0x97`). The early-return form emits them inline. 5. **The switch case order is `10/13`, `8`, `4`.** MSVC lays case bodies out in source order, and January's are execute → backspace → goodbye. Writing them in ascending value order reverses the layout: this alone took `_code_001203c0` from 219 to 120 differing instructions. Also: **the `transport_address` is zeroed inline in an inner scope.** `csmemset(&address, …)` emits a real call (+2 relocations, 208 B); declaring `= {0}` at function scope emits the zeroing before the globals `csmemset`. An inner block opened after the endpoint check puts it where January has it. ## 4. `transport.h` — union member order `struct transport_address_data`'s union originally listed `byte bytes[16]` first. With that order, `struct transport_address address = {0}` makes MSVC initialise the union's *first member* — so it emits a byte store for `bytes[0]` followed by a misaligned blob fill (`[ebp-0x17]`, `[ebp-0x13]`, …). January's fill is six clean 4-aligned dword stores. Reordering the union to put `unsigned long long_words[4]` first restores the aligned fill and matches January. The union order is not observable except through codegen. The admission lane forced clean rebuilds of both current exact consumers: all 5 `transport_address.obj` functions and all 100 `ui_widget_event_handler_functions.obj` functions remained strict-exact. ## 5. The residual as it was diagnosed — SUPERSEDED BY §6 **This section's class verdict was wrong.** Both functions were adjudicated here as register-allocation ties; both closed from source (§6). The measured negatives below remain valid negatives and are kept. Two specific claims are corrected: (a) the two functions were *not* "identical modulo register naming" — `_code_001203c0` had two `push 0` where January has `push eax` (a 2-byte code-size difference hidden by section alignment padding, which also displaced the jump table), and (b) the register assignment was not a tie — it was downstream of a live range the source failed to create, exactly the house-rules §6 pattern "read the live ranges, never chase the registers". ### `_code_001203c0` January puts `index` in EDI and `size` in EBX; we do the reverse. A mechanical EBX↔EDI rename of our disassembly aligns **132 of 172** instructions, and the residue shows it is *not* a clean swap: at the execute case both builds use EDI for the buffer pointer (`lea edi,[esi+4]`). January therefore **reuses EDI for both `index` and the buffer pointer**, spilling `index` and reloading `size` from `[ebp+0xc]` each iteration; we keep the two in separate registers. Same instruction sequence, different live-range packing. Verified inert against it: | attempt | ndiff | |---|--:| | baseline | 120 | | `index` declared before `result` | 120 | | `length` at function scope | 120 | | `character` and `length` both at function scope | 120 | | named `client_buffer` local in the append path | 119 | | cached `limit` local for the bound | 119 | | `buffer[index]` directly, no pointer variable | 119 | | `size>index` | 119 | | `indexindex` emits `cmp ebx,edi; jle` where January has `cmp edi,ebx; jge`, so `indexendpoint, character, 1); if (length<=0) { error(2, "failed to write to telnet client ('%s')", transport_error_to_string((short)length)); ... } ``` (and likewise at the overflow-message site, reusing the `length` local), the function became **byte-exact in one step**. The named result's live range now crosses into the error arm, and that alone flipped the entire EBX/EDI assignment and the prologue push schedule into January's shape — the "120 differing instructions" were all downstream of these two calls. The tell in the disassembly was 1-byte `push eax` (0x50) vs 2-byte `push 0` (0x6A 00) with every later branch target off by one, and section size still 496 because alignment padding absorbed the difference. **Size-exactness can hide a real code-size delta; diff the padding tail too.** (The earlier §5 claim that January "reuses EDI for both `index` and the buffer pointer" was an artifact of the mechanical rename: January converts `index` to the character pointer in place (`add edi, eax`) exactly as we do, and the append-path buffer pointer lands in EBX. The execute path's `lea edi,[esi+4]` matched byte-for-byte on both sides while meaning different things — jan's EDI held the dead character pointer, ours the dead size copy.) ### `_telnet_console_initialize`: `{{0}}`, not `{0}` January's zero-fill of the `transport_address` local splits **exactly at the member boundary**: `[-0x18]` immediate + `[-0x14..-0xc]` from ECX cover the 16-byte `struct transport_address_data address` member, then `lea ecx,[ebp-0x18]` consumes ECX, then `[-8]`/`[-4]` (the trailing `address_length`/`port`/`address_type` scalars) come from a freshly materialised EDX. Two zero sources for one initializer means two fill operations in the IR. **VC7 groups a `= {…}` zero-fill by brace level**: - `= {0}` → the `0` initialises the deepest first scalar (`long_words[0]`, the immediate store) and the *rest of the whole struct* is one fill — one zero register (ours, wrong); - `= {{0}}` → member 0's fill and the implicit zeroing of the remaining members are *separate* fills — the allocator can hand the address `lea` the first fill's dead register and materialise a second zero for the tail (January, exact). One character of source. The three error-path register rotations §5 describes were pure cascade from this and closed with it. ### Verification Whole unit rebuilt via ninja (CL path patched per the known checkout issue) and every one of the 17 target sections verified with the strict comparator (`section_infos_equal`): **17/17 EXACT**, 5/5 functions, 1344/1344 `.text` bytes. `configure.py progress` validation (parked/semantic/ownership gates) passes. Status flipped `NonMatching` → `Matching`. The §5 compiler A/B result (all three local VC7 suites byte-identical) stands — correctly, as it turns out: the residual was never compiler variance. ### House-rule audit Every parameter remains one-per-line, both no-argument `void` parameter lists use the project form, and every void function ends in an explicit `return;`. The source contains no assembly, volatile codegen forcing, alignment trick, compiler-flag override, or raw typed `tag_get`/`object_get` cast. The early returns in `telnet_console_print`, `telnet_console_process`, and `code_001203c0` are measured control-flow requirements of the exact January sections; rewriting them into one exit changes the emitted branches and is the documented exception to the preferred single-return rule. > Note: `ninja` cannot launch `xbox/bin/vc7/CL.Exe` in this checkout > (`CreateProcess failed`); pre-existing, reproduces in `campaign-integration`. > Verification used CL directly with the exact cflags ninja generates. ## 7. Current-campaign independent admission audit (2026-08-13) The completed reconstruction was transplanted onto campaign tip `738deff8` and rebuilt from source with XDK 3911 `CL.Exe` 13.00.9254.1 and the unchanged `/O2 /Oy- /DDEBUG /Dxbox` flags. This audit did not trust the earlier report: it enumerated every external, target-owned COFF section and compared each one again with `tools/coff_compare.py::section_infos_equal`. All **17/17 target-owned sections** are strict-equal: the five functions (1344 padded code bytes, 106 relocations), all eleven string COMDATs (473 bytes, no relocations), and `_bss_00456d00` (140 bytes, no relocations). The function measurements from this rebuild are: | function | padded bytes | relocations | normalized SHA-256 | |---|---:|---:|---| | `_telnet_console_initialize` | 224 | 18 | `2b614d5f0126ba4234219a23adcb5e98ec7e39b2feab42b900006feab1ed1748` | | `_telnet_console_dispose` | 80 | 7 | `bb37c5b76e11f4f7badf0fbdf0de442d5af90930871d0645d3412b483e279a2a` | | `_telnet_console_print` | 192 | 18 | `5509b44b5d0c4ffbcda8b091d14df2ed82d3dd1748b0e7a6757edd28b4e93657` | | `_code_001203c0` | 496 | 30 | `320e28baa4ff4a0f80d2a37ce9c2107d66c72e2bf5c321c8f205d813d25fc398` | | `_telnet_console_process` | 352 | 33 | `6697a49e8c92a112af1c2dce74fa24f03bcbf0c7c6c014844f69c4ae25eb6715` | The ordinary objdiff reporter still scores `_code_001203c0` at 89.14286% because its switch-table/local-label ownership is encoded differently. The hardened comparator proves all 496 padded bytes and all 30 resolved relocation destinations equal, so the current fail-closed semantic ledger records that specific false negative. `tools.audit_semantic_matches` independently accepts it from `semantic-coff` evidence; after that entry, `ninja progress` credits the complete object instead of revoking it. The only shared-header change reorders the three equivalent views in `transport_address_data` so the dword view is the aggregate's first member. The complete current-tree include census found exactly one other C consumer, `transport_address.c`. It was force-rebuilt, and all **42/42** of its owned sections remained strict-equal (five functions, all string COMDATs, and its 256-byte BSS). This closes the change's measured regression radius. Plainly: the code, its strings, its zero-initialized globals, and every call or data reference all describe the same January object. The one progress-report disagreement is a label-spelling artifact, not a byte or destination mismatch. ### Current house/Berth audit - every parameter is on its own line, and every no-argument `(void)` is on its own line; - every void function has an explicit final `return;`; - the few early returns are the measured exceptions required to reproduce the original branch layout and are documented above; - no inline assembly, `volatile`, `__forceinline`, undefined behavior, byte-forcing, alignment pragma, flag override, raw object/tag cast, or magic field-offset access was introduced; - recovered numeric values are named protocol/domain constants, and recovered storage is represented by readable structures rather than offset arithmetic. ### Final current-campaign gates - `ninja halobetacache_build`: passed after rebuilding all 465 base objects. - `ninja progress`: credited `source/networking/telnet_console` as the 257th complete Halo unit; halobetacache is 20.26% object-matched (257/468), with 358614/1770166 code bytes and 3190/7574 functions credited. - `tools.regression_gate snapshot/check --unit source/networking/telnet_console`: passed with five `still_exact`, no changed nonexact functions, no failures, and no warnings. - `python -m unittest discover -s tools -p 'test_*.py' -v`: 173/173 passed. ## Compiler A/B — all three local VC7 suites produce identical bytes Prompted by the `units.obj` compiler-provenance work (`units-campaign-current-20260802/docs/object_matching_logs/units_obj_compiler_provenance.md` and `units_obj_compiler_qfe_hunt.md`), the local worktrees were re-censused for distinct compiler binaries. **Three** distinct `CL.Exe` images exist, not one: | suite | copies | `CL.Exe` SHA-256 (16) | note | |---|--:|---|---| | 13.00.9254.1 | 830 | `483e00c47bb08d69` | project standard | | 13.00.9210 | 4 | `38955691c987c64d` | genuinely different `C1`/`C2` back end | | vsnet-beta2-rc4-9254 | 1 | `cd6b42c2e4f14140` | reports 9254 but is a distinct image | This object was rebuilt with all three complete suites (each with its matching `C1.dll` / `C1xx.dll` / `C2.dll`), same flags, same source. **All three produce byte-identical output**, with the identical residual. That is a stronger park than "only one compiler is available": the residual is now shown not to be compiler-version variance at all. It also corrects an earlier claim in this log that only a single CL binary existed locally — that was wrong, and the conclusion it supported (a QFE-level codegen difference) is not supported by this test.