nx_dwarf_line.nx
buildroot/runtime/nx_dwarf_line.nx
about
nx_dwarf_line.nx -- DWARF v5 .debug_line section builder.
Maps machine-instruction addresses back to source-file line + col.
Enables `gdb` (and our future sovereign debugger) to show the .nx
source line when stopped at any RV64 PC.
DWARF v5 line program (DWARF 5 spec ยง6.2):
.debug_line consists of one "line number program" per compilation
unit. Header (~30 bytes), then a sequence of standard + extended
opcodes that build a "line table" via a small VM:
state: address (u64), op_index, file, line, column,
is_stmt, basic_block, end_sequence, prologue_end,
epilogue_begin, isa, discriminator
Standard opcodes drive the VM:
DW_LNS_copy commit current row
DW_LNS_advance_pc += operand uleb128
DW_LNS_advance_line += signed leb
DW_LNS_set_file := operand
DW_LNS_set_column := operand
... etc
Why now (R2 capability lever):
* Managed platforms (V8/WASM) can't give us proper PC-to-source
debugging at the silicon level. We can.
* Pairs with nx_dbg.nx (future) for full sovereign debugging:
read .debug_line, decode VM, print source location at faults.
* F6-deterministic: same source -> same .debug_line bytes.
What we ship here (v0.0.1):
* Builder for the .debug_line bytes (header + line program).
* uleb128 / sleb128 encoders (used widely in DWARF).
* Helper to register (address, file, line) tuples and finalize.
What we DEFER (separate commits):
dependencies 1 imports · 1 importers
imports: syscalls.nx
imported by: nx_addr2line.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 88 | struct NxDwLine |
consts
| 58 | const DW_LNS_copy: i64 = 0x01 |
| 59 | const DW_LNS_advance_pc: i64 = 0x02 |
| 60 | const DW_LNS_advance_line: i64 = 0x03 |
| 61 | const DW_LNS_set_file: i64 = 0x04 |
| 62 | const DW_LNS_set_column: i64 = 0x05 |
| 63 | const DW_LNS_negate_stmt: i64 = 0x06 |
| 64 | const DW_LNS_set_basic_block: i64 = 0x07 |
| 65 | const DW_LNS_const_add_pc: i64 = 0x08 |
| 66 | const DW_LNS_fixed_advance_pc: i64 = 0x09 |
| 67 | const DW_LNS_set_prologue_end: i64 = 0x0A |
| 68 | const DW_LNS_set_epilogue_begin: i64 = 0x0B |
| 69 | const DW_LNS_set_isa: i64 = 0x0C |
| 72 | const DW_LNE_end_sequence: i64 = 0x01 |
| 73 | const DW_LNE_set_address: i64 = 0x02 |
| 74 | const DW_LNE_set_discriminator: i64 = 0x04 |
| 79 | const NX_DW_LINE_BASE: i64 = -5 |
| 80 | const NX_DW_LINE_RANGE: i64 = 14 |
| 81 | const NX_DW_OPCODE_BASE: i64 = 13 |
| 82 | const NX_DW_MIN_INST_LEN: i64 = 1 |
| 83 | const NX_DW_MAX_OPS_PER_INST: i64 = 1 |
| 84 | const NX_DW_DEFAULT_IS_STMT: i64 = 1 |
| 97 | const NX_DWLINE_BYTES: i64 = 48 |
functions
| 99 | func nx_dwline_new(cap: i64) -> *NxDwLine called by 1: main |
| 113 | func nx_dwline_byte(d: *NxDwLine, v: i64) -> i64 |
| 121 | func nx_dwline_uleb(d: *NxDwLine, v: i64) -> i64 |
| 135 | func nx_dwline_sleb(d: *NxDwLine, v: i64) -> i64 |
| 154 | func nx_dwline_advance_pc(d: *NxDwLine, delta: i64) -> i64 |
| 163 | func nx_dwline_advance_line(d: *NxDwLine, delta: i64) -> i64 |
| 171 | func nx_dwline_set_file(d: *NxDwLine, file_idx: i64) -> i64 |
| 179 | func nx_dwline_copy(d: *NxDwLine) -> i64 |
| 184 | func nx_dwline_end_sequence(d: *NxDwLine) -> i64 |
| 193 | func nx_dwline_set_address(d: *NxDwLine, addr: i64) -> i64 |
| 215 | func nx_dwline_record(d: *NxDwLine, addr: i64, file_idx: i64, line: i64) -> i64 |
| 233 | func nx_dwline_size(d: *NxDwLine) -> i64 { return d.used } called by 1: main |
| 234 | func nx_dwline_bytes(d: *NxDwLine) -> *u8 { return d.buf } |
| 238 | func main() -> i64 |