code wiki / (root) / nx_gdbstub.nx

nx_gdbstub.nx

buildroot/runtime/nx_gdbstub.nx

40336 B868 linesdepth 6pulls 19 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_gdbstub.nx -- GDB REMOTE SERIAL PROTOCOL stub over the sovereign RV64 emulator (rv64im_min_sim). THE RUNG: /compare/nishios NO1 "GDB remote-serial stub", watch symbol gdbstub_serve (ver 0.1 N0, ranked #1 2026-08-23). Its accept rule is an EXTERNAL ORACLE: a STOCK gdb attaches to a guest running on our emulator, breaks at an address our own decoder resolved, steps, reads a register whose value an independent sim run derived, and detaches leaving the guest running -- plus the negative control that a breakpoint at a never-executed address does NOT stop. That oracle lives in nx_gdbstub_gate (gdb-multiarch in batch mode); this file is the stub it attaches to. WHAT IT SPEAKS (GDB Remote Serial Protocol, the subset a stock gdb needs for a bare-metal target): framing $<payload>#<2-hex checksum>, '+'/'-' acks until QStartNoAckMode, 0x03 = interrupt qSupported PacketSize + QStartNoAckMode + qXfer:features:read (we SERVE target.xml, so gdb never has to guess the register file: riscv:rv64, x0..x31 + pc, 64-bit) ? g G p P m M Z0 z0 s c D k vKill H T qC qAttached qfThreadInfo qsThreadInfo stop replies: S05 (trap: breakpoint or single-step), S02 (interrupted by 0x03), W<code> (guest halted through the finisher / exit ecall), X04 (illegal instruction) everything else answers the empty packet, which is the protocol's "unsupported" and makes gdb fall back (X -> M, vCont -> s/c) rather than fail. WHAT IT DELIBERATELY DOES NOT DO: - memory packets (m/M) touch RAM ONLY (mem_base..mem_base+mem_size): a debugger read of device MMIO would run the device model's side effects (a UART read clears state), and a debugger must not change what it observes. Device windows answer E14. Addresses are translated through the sim's own nx_rv64im_xlate, so in S/U-mode under Sv39 gdb sees the guest's virtual space, same as the guest does; a walk fault answers E14 and the fault flag is cleared, never left for the guest. (Documented imprecision: the sim's xlate sets the PTE A bit on a successful walk, so an S/U-mode 'm' read can set A on a page the guest had not yet touched.) - no RLE in replies, no binary X writes, no threads beyond the one hart (qC/H answer thread 1). SIZES ARE DERIVED, NOT PICKED: GDB_G_HEX = (NX_RV64IM_RF_N_REGS + 1 pc) * (NX_RV64IM_RF_WIDTH / 4) hex chars = the 'g' reply GDB_PACKETSIZE (advertised) = GDB_G_HEX: gdb bounds every m/M/qXfer chunk by this, so a reply never exceeds the 'g' reply and a request payload never exceeds it either. GDB_BUF = 2*GDB_PACKETSIZE + framing: payload bound + command prefix (shorter than a payload by construction) + '$' '#' and two checksum chars. breakpoints = a BITMAP over RAM, one bit per halfword (the RVC granularity), sized from sim.mem_size -- no breakpoint cap to guess, O(1) test in the hot step loop. GDB_POLL_STEPS = the step count between interrupt polls while continuing, = one millisecond at the 20 MIPS interpreted rate nishios.plan publishes, so Ctrl-C latency is bounded to ~1 ms without a syscall per step.

dependencies 3 imports · 0 importers

nx_syscalls.nx nx_http_server.nx nx_bootcap.nx nx_gdbstub.nx

imports: nx_syscalls.nxnx_http_server.nxnx_bootcap.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main gs_puts sys_write gs_len sys_mmap nxa_die sys_write ↻ sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_munmap sys_close bootcap_machine bootcap_machine_mem gs_putn sys_mmap ↻ sys_write ↻ sys_munmap ↻ gdbstub_serve gs_new sys_mmap ↻ gs_build_xml gs_xml_emit gs_xcat gs_len ↻ gs_cat

structs

190struct NxGdbStub

consts

58const GDB_CH_DOLLAR: i64 = 36 // '$' frame start
59const GDB_CH_HASH: i64 = 35 // '#' frame end
60const GDB_CH_PLUS: i64 = 43 // '+' ack
61const GDB_CH_MINUS: i64 = 45 // '-' nak
62const GDB_CH_INT: i64 = 3 // 0x03 interrupt (gdb Ctrl-C)
63const GDB_CH_COMMA: i64 = 44
64const GDB_CH_COLON: i64 = 58
65const GDB_CH_SEMI: i64 = 59
66const GDB_CH_EQ: i64 = 61
69const GDB_HEX_CHARS_PER_BYTE: i64 = 2
70const GDB_BITS_PER_BYTE: i64 = 8
71const GDB_HEX_PER_REG: i64 = NX_RV64IM_RF_WIDTH / GDB_BITS_PER_BYTE * GDB_HEX_CHARS_PER_BYTE // 64-bit reg -> 16 hex chars
72const GDB_PC_REGNUM: i64 = NX_RV64IM_RF_N_REGS // gdb's riscv numbering: x0..x31 then pc
73const GDB_N_REGS: i64 = NX_RV64IM_RF_N_REGS + 1 // the 'g' packet: every GPR + pc
74const GDB_G_HEX: i64 = GDB_N_REGS * GDB_HEX_PER_REG // the 'g' reply length
75const GDB_PACKETSIZE: i64 = GDB_G_HEX // advertised bound on every payload
76const GDB_FRAMING: i64 = 4 // '$' + '#' + 2 checksum chars
77const GDB_BUF: i64 = GDB_PACKETSIZE * 2 + GDB_FRAMING // payload + command prefix + framing
78const GDB_MIPS_PUBLISHED: i64 = 20 // interpreted rate, nishios.plan pos| (measured)
79const GDB_STEPS_PER_MS_AT_1MIPS: i64 = 1000
80const GDB_POLL_STEPS: i64 = GDB_MIPS_PUBLISHED * GDB_STEPS_PER_MS_AT_1MIPS // = 1 ms of guest time between interrupt polls
81const GDB_LISTEN_BACKLOG: i64 = 1 // one debugger at a time, by design
82const GDB_BYTE_MASK: i64 = 255
83const GDB_SOCKADDR_LEN: i64 = 16
84const GDB_SYS_GETSOCKNAME_RV64: i64 = 204 // rv64 number; x86ctx translates 204 -> 51
87const GDB_EXIT_OK: i64 = 0
88const GDB_EXIT_USAGE: i64 = 2
89const GDB_EXIT_NOIMAGE: i64 = 3
90const GDB_EXIT_LISTEN: i64 = 4
91const GDB_EXIT_KILLED: i64 = 5
215const GDB_STUB_BYTES: i64 = 256 // 24 fields * 8 = 192; room for 8 more -- sized like NX_RV64IM_SIM_BYTES

functions

94func gs_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
95func gs_puts(s: *u8) -> i64 { sys_write(1, s, gs_len(s)); return 0 }
96func gs_putn(v: i64) -> i64
114func gs_hexnib(n: i64) -> i64 { if n < 10 { return 48 + n } return 87 + n } // '0'..'9', 'a'..'f'
115func gs_hexval(c: i64) -> i64
122func gs_put_le64(b: *u8, o: i64, v: i64) -> i64
called by 2: gs_do_ggs_do_p calls 1: gs_hexnib
135func gs_get_le64(b: *u8, o: i64, err: *i64) -> i64
called by 2: gs_do_Ggs_do_P calls 1: gs_hexval
151func gs_parse_hex(b: *u8, o: i64, n: i64, endp: *i64, err: *i64) -> i64
168func gs_cat(d: *u8, o: i64, s: *u8) -> i64
174func gs_starts(b: *u8, n: i64, s: *u8) -> i64
183func gs_eq(b: *u8, n: i64, s: *u8) -> i64
called by 1: gs_session calls 2: gs_startsgs_len
218func gs_xcat(d: *u8, o: i64, measure_only: i64, s: *u8) -> i64
called by 1: gs_xml_emit calls 2: gs_lengs_cat
222func gs_xcatn(d: *u8, o: i64, measure_only: i64, v: i64) -> i64
called by 1: gs_xml_emit calls 2: sys_mmapsys_munmap
237func gs_abi_name(i: i64) -> *u8
called by 1: gs_xml_emit
274func gs_xml_emit(x: *u8, measure_only: i64) -> i64
295func gs_build_xml(st: *NxGdbStub) -> i64
called by 1: gs_new calls 2: gs_xml_emitsys_mmap
304func gs_new(sim: *NxRv64imSim) -> *NxGdbStub
called by 1: gdbstub_serve calls 2: sys_mmapgs_build_xml
333func gs_bp_ok(st: *NxGdbStub, addr: i64) -> i64
called by 2: gs_bp_testgs_bp_set
338func gs_bp_test(st: *NxGdbStub, addr: i64) -> i64
called by 2: gs_bp_setgs_resume calls 1: gs_bp_ok
346func gs_bp_set(st: *NxGdbStub, addr: i64, on: i64) -> i64
called by 1: gs_do_Z calls 2: gs_bp_okgs_bp_test
363func gs_fill(st: *NxGdbStub) -> i64
called by 3: gs_sendgs_recvgs_poll_int calls 1: sys_read
370func gs_consume(st: *NxGdbStub, n: i64) -> i64
called by 2: gs_sendgs_recv
379func gs_send(st: *NxGdbStub, payload: *u8, n: i64) -> i64
420func gs_send_str(st: *NxGdbStub, s: *u8) -> i64 { return gs_send(st, s, gs_len(s)) }
421func gs_send_empty(st: *NxGdbStub) -> i64 { return gs_send(st, st.tx, 0) }
426func gs_recv(st: *NxGdbStub, outlen: *i64) -> i64
484func gs_reply_halt(st: *NxGdbStub) -> i64
496func gs_reply_trap(st: *NxGdbStub) -> i64 { return gs_send_str(st, "S05" as *u8) }
called by 2: gs_resumegs_session calls 1: gs_send_str
497func gs_reply_int(st: *NxGdbStub) -> i64 { return gs_send_str(st, "S02" as *u8) }
called by 1: gs_resume calls 1: gs_send_str
500func gs_poll_int(st: *NxGdbStub) -> i64
534func gs_resume(st: *NxGdbStub, single_step: i64) -> i64
558func gs_ram_off(st: *NxGdbStub, vaddr: i64) -> i64
called by 2: gs_do_mgs_do_M
569func gs_do_g(st: *NxGdbStub) -> i64
called by 1: gs_session calls 2: gs_put_le64gs_send
577func gs_do_G(st: *NxGdbStub, n: i64) -> i64
593func gs_do_p(st: *NxGdbStub, n: i64) -> i64
606func gs_do_P(st: *NxGdbStub, n: i64) -> i64
620func gs_do_m(st: *NxGdbStub, n: i64) -> i64
644func gs_do_M(st: *NxGdbStub, n: i64) -> i64
676func gs_do_Z(st: *NxGdbStub, n: i64, on: i64) -> i64
687func gs_do_qSupported(st: *NxGdbStub) -> i64
700func gs_do_qXfer(st: *NxGdbStub, n: i64) -> i64
724func gs_session(st: *NxGdbStub) -> i64
773func gdbstub_serve(sim: *NxRv64imSim, port: i64, readyfile: *u8) -> i64
827func main(argc: i64, argv: *i64) -> i64