code wiki / (root) / nx_dwarf_str.nx

nx_dwarf_str.nx source

↩ module page · 148 lines · 5330 B

1// nx_dwarf_str.nx -- DWARF .debug_str + .debug_line_str builder. 2// 3// DWARF stores its strings in dedicated sections (.debug_str / 4// .debug_line_str) so DIE entries (.debug_info) and line-program 5// file names (.debug_line) can reference them by 4- or 8-byte 6// offset instead of inlining bytes. Crucial for keeping debug 7// info compact when many DIEs share names ("int", "char *", 8// function names, etc.). 9// 10// API matches nx_strtab but with two ergonomic differences: 11// 12// * Dedup via nx_intern on the input bytes. Same input string 13// always returns the same offset, so DIE-side references 14// hash-cons cleanly. 15// 16// * `intern_at_in_pool` returns the OFFSET (the value DWARF 17// wants), not a *u8. 18// 19// Pairs with nx_dwarf_line.nx (file-name strings) and the 20// forthcoming nx_dwarf_info.nx (DIE attribute strings). 21 22// nx_safety_envelope: 23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 24// sil_target: SIL1 25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 26// verdict: NOT_YET_EVALUATED 27 28import "syscalls.nx" 29import "nx_intern.nx" 30const NX_MAGIC_1024: i64 = 1024 31 32struct NxDwStr { 33 pool: *NxIntern, 34 flat: *u8, // backing flat .debug_str-shape buffer 35 flat_used: i64, // bytes used in flat 36 flat_cap: i64, 37} 38 39const NX_DWSTR_BYTES: i64 = 32 40 41func nx_dwstr_new(bytes_cap: i64, ids_cap: i64) -> *NxDwStr { 42 let raw: *u8 = sys_mmap(NX_DWSTR_BYTES) 43 let s: *NxDwStr = raw as *NxDwStr 44 s.pool = nx_intern_new(bytes_cap, ids_cap) 45 s.flat = sys_mmap(bytes_cap) 46 s.flat[0] = 0 // DWARF .debug_str begins with a NUL too 47 s.flat_used = 1 48 s.flat_cap = bytes_cap 49 return s 50} 51 52// Insert (or look up) `bytes`; return the byte OFFSET in the 53// .debug_str section. -1 on overflow. 54func nx_dwstr_get(s: *NxDwStr, bytes: *u8, len: i64) -> i64 { 55 // Dedup via the intern pool. 56 let id: i64 = nx_intern_get(s.pool, bytes, len) 57 if id < 0 { return -1 } 58 59 // For NEW ids we must also append to the flat buffer. Since 60 // intern_get returns dense ids (0..n), we know id == n_ids - 1 61 // means we just inserted; in that case append to flat too. 62 let n_ids_now: i64 = nx_intern_count(s.pool) 63 if id == n_ids_now - 1 { 64 if s.flat_used + len + 1 > s.flat_cap { return -1 } 65 let off: i64 = s.flat_used 66 var i: i64 = 0 67 while i < len { 68 s.flat[off + i] = bytes[i] 69 i = i + 1 70 } 71 s.flat[off + len] = 0 72 s.flat_used = off + len + 1 73 return off 74 } 75 76 // Already-interned: walk via lookup to find the offset. 77 // Simple linear scan -- fine for v0.0.1. A future commit 78 // will add a parallel id->offset side-table. 79 var off: i64 = 1 80 var k: i64 = 0 81 while k < id { 82 var l: i64 = 0 83 while s.flat[off + l] != 0 { l = l + 1 } 84 off = off + l + 1 85 k = k + 1 86 } 87 return off 88} 89 90// Convenience: NUL-terminated C-string. 91func nx_dwstr_get_cstr(s: *NxDwStr, str: *u8) -> i64 { 92 var n: i64 = 0 93 while str[n] != 0 { n = n + 1 } 94 return nx_dwstr_get(s, str, n) 95} 96 97// Total bytes used by the .debug_str section (caller copies these 98// into the section buffer at link time). 99func nx_dwstr_size(s: *NxDwStr) -> i64 { return s.flat_used } 100func nx_dwstr_bytes(s: *NxDwStr) -> *u8 { return s.flat } 101 102// Read back: get the *u8 of the string starting at `offset`. 103func nx_dwstr_at(s: *NxDwStr, offset: i64) -> *u8 { 104 let base: i64 = (s.flat as i64) + offset 105 return base as *u8 106} 107 108// ---- self-test --------------------------------------------------- 109 110func main() -> i64 { 111 let s: *NxDwStr = nx_dwstr_new(NX_MAGIC_1024, 64) 112 113 // Initial: just the NUL at offset 0, size 1. 114 if nx_dwstr_size(s) != 1 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 115 116 // Append "main" -> offset 1, size 1+4+1=6. 117 let main_str: *u8 = sys_mmap(8) 118 main_str[0] = 0x6D; main_str[1] = 0x61; main_str[2] = 0x69; main_str[3] = 0x6E 119 let off_main: i64 = nx_dwstr_get(s, main_str, 4) 120 if off_main != 1 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 121 if nx_dwstr_size(s) != 6 { return __syscall(93, 21, 0, 0, 0, 0, 0) } 122 123 // Append "puts" -> offset 6, size 6+4+1=11. 124 let puts_str: *u8 = sys_mmap(8) 125 puts_str[0] = 0x70; puts_str[1] = 0x75; puts_str[2] = 0x74; puts_str[3] = 0x73 126 let off_puts: i64 = nx_dwstr_get(s, puts_str, 4) 127 if off_puts != 6 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 128 129 // Re-append "main" -> SAME offset (dedup). 130 let off_main2: i64 = nx_dwstr_get(s, main_str, 4) 131 if off_main2 != 1 { return __syscall(93, 40, 0, 0, 0, 0, 0) } 132 if nx_dwstr_size(s) != 11 { return __syscall(93, 41, 0, 0, 0, 0, 0) } 133 134 // Read back at offset 1: "main\0". 135 let read_main: *u8 = nx_dwstr_at(s, 1) 136 if read_main[0] != 0x6D { return __syscall(93, 50, 0, 0, 0, 0, 0) } 137 if read_main[3] != 0x6E { return __syscall(93, 51, 0, 0, 0, 0, 0) } 138 if read_main[4] != 0 { return __syscall(93, 52, 0, 0, 0, 0, 0) } 139 140 // C-string convenience. 141 let c_str_raw: *u8 = sys_mmap(8) 142 c_str_raw[0] = 0x66; c_str_raw[1] = 0x6F; c_str_raw[2] = 0x6F // "foo\0" 143 c_str_raw[3] = 0 144 let off_foo: i64 = nx_dwstr_get_cstr(s, c_str_raw) 145 if off_foo != 11 { return __syscall(93, 60, 0, 0, 0, 0, 0) } 146 147 return 0 148}