code wiki / _hdl_build / nx_gdbstub_gate.nx
nx_gdbstub_gate.nx
buildroot/runtime/_hdl_build/nx_gdbstub_gate.nx
about
nx_gdbstub_gate.nx -- the VERDICT gate for NO1 (nx_gdbstub, watch symbol gdbstub_serve). NO mocks.
THE ACCEPT RULE (nishios.plan NO1, verbatim): a STOCK gdb attaches to a running guest, breaks at an
address the disassembler independently resolved, steps, reads a register whose value a separate path
derived, and detaches leaving the guest running -- plus a NEG-CONTROL that a breakpoint at an address
never executed does NOT stop. The oracle is a third-party debugger, so this rung cannot be self-graded.
HOW EACH EXPECTATION IS DERIVED INDEPENDENTLY OF THE STUB (the stub is never its own oracle):
bp address = the pc of the FIRST store-kind instruction the K-R0 boot image executes, found by
running a SECOND, stub-free sim in this process and DECODING each fetched word with
rv64im_min_decoder (nx_rv64im_decode_kind == NX_RV64IM_OP_STORE).
register = that store's rs2 (decoded) read from the reference sim's regfile at that pc.
next pc = bp + 4, after asserting the word is a 32-bit encoding ((w & 3) == 3) and a store never branches.
memory words = the first two little-endian words of the IMAGE FILE, not of any sim.
never-addr = the lowest 4-aligned RAM address at/after the image end that the reference run NEVER
fetched (a bitmap of every executed pc over the WHOLE boot, not a guess).
golden = <image>.gold, the transcript the boot rulers already certify.
SESSIONS (each on a FRESH stub process, port 0 = kernel-chosen, read back from the stub's ready file):
A stock gdb: attach, break *bp, continue, p/x $xN, stepi, x/2xw mem_base, detach -> T1..T7
B stock gdb: break *never-addr, continue -> the guest must run to EXIT, never a breakpoint stop -> T8
C sovereign RSP client (no gdb): a frame with a WRONG checksum must be NAK'd; a good '?' must be
ACK'd and answered S05; 'D' must be answered OK; the stub must count the bad frame -> T9..T12
If no stock gdb is on this host, sessions A/B are a MISSING PRECONDITION (gv_need -> SKIP), never a
pass and never a RED: the oracle leg is reported UNWITNESSED, C still runs. (NAS: no gdb; laptop WSL:
gdb-multiarch 15.1 installed 2026-08-23 as a third-party ORACLE only.)
Fixtures live in /tmp/nx_gdbstub_gate/ (created at setup; a crashed run cannot leave a fixture beside a
production beat). Every tooth prints the values it compared, not only PASS/FAIL.
license_tier: ORIGINAL (the debugger is an oracle; nothing of it ships)
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_gatekit_lib.nxnx_http_server.nxnx_bootcap.nxrv64im_min_decoder.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 38 | const GG_DIR: *u8 = "/tmp/nx_gdbstub_gate" |
| 39 | const GG_IMG: *u8 = "runtime/_hdl_build/_boot_nishi_virt.bin" |
| 40 | const GG_IMG_ALT: *u8 = "_boot_nishi_virt.bin" |
| 41 | const GG_OUT_CAP: i64 = 1048576 // gdb's batch output for a 1,164-step boot is a few KB; 1 MiB = the gatekit capture bound |
| 44 | const GG_READY_MS: i64 = 5000 |
| 45 | const GG_READY_POLL_MS: i64 = 50 |
| 48 | const GG_GDB_MS: i64 = 60000 |
| 49 | const GG_IO_MS: i64 = 5000 // session C: one reply should arrive within this; a stub that answers nothing fails the tooth |
| 50 | const GG_RV_INSTR_BYTES: i64 = 4 |
| 51 | const GG_EXIT_CODE_RD: i64 = 0 |
functions
| 53 | func gg_p(s: *u8) -> i64 { return gv_puts(s) } |
| 54 | func gg_n(v: i64) -> i64 { return gv_num(v) } |
| 55 | func gg_hex(v: i64) -> i64 |
| 68 | func gg_find(buf: *u8, n: i64, from: i64, needle: *u8) -> i64 |
| 81 | func gg_hexval(c: i64) -> i64 called by 1: gg_parse_hex |
| 88 | func gg_parse_hex(buf: *u8, n: i64, i: i64, ndig: *i64) -> i64 |
| 105 | func gg_value_after(buf: *u8, n: i64, needle: *u8, k: i64, found: *i64) -> i64 |
| 129 | func gg_resolve(out: *u8, a: *u8, b: *u8, c: *u8) -> i64 |
| 138 | func gg_spawn(elf: *u8, a1: *u8, a2: *u8, a3: *u8, logpath: *u8) -> i64 |
| 158 | func gg_wait_port(readyfile: *u8) -> i64 |
| 176 | func gg_reap(pid: i64, budget_ms: i64) -> i64 |
| 193 | func gg_rsp_connect(port: i64) -> i64 |
| 203 | func gg_rsp_read(fd: i64, buf: *u8, cap: i64, want: i64) -> i64 |
| 221 | func main() -> i64 |