code wiki / (root) / nx_dwarf_pubnames.nx

nx_dwarf_pubnames.nx source

↩ module page · 168 lines · 5685 B

1// nx_dwarf_pubnames.nx -- DWARF .debug_pubnames + .debug_pubtypes builder. 2// 3// Companion to .debug_aranges (PC-to-CU lookup): .debug_pubnames 4// gives gdb/lldb a NAME-to-DIE-offset lookup, so when the user 5// types `b foo`, the debugger doesn't scan every CU's DIE forest 6// to find the function `foo` -- it consults this index first. 7// 8// Per-CU layout (DWARF v4 6.1.1; v5 deprecated this in favor of 9// .debug_names but most consumers still understand pubnames): 10// 11// length u32 (excludes itself) 12// version u16 = 2 13// debug_info_off u32 (offset of CU header in .debug_info) 14// debug_info_len u32 (length of CU contribution to .debug_info) 15// tuples: 16// die_offset u32 (offset within the CU) 17// name NUL-terminated string 18// terminator u32 (=0) 19// 20// .debug_pubtypes uses the same layout but for type names instead. 21// The two builders share this code -- caller decides which section 22// the bytes go into. 23 24// nx_safety_envelope: 25// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 26// sil_target: SIL1 27// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 28// verdict: NOT_YET_EVALUATED 29 30import "syscalls.nx" 31 32const NX_DWPUB_VERSION: i64 = 2 33 34struct NxDwPub { 35 buf: *u8, 36 len: i64, 37 cap: i64, 38 cu_start: i64, 39 cu_open: i64, 40} 41 42const NX_DWPUB_BYTES: i64 = 40 43 44func nx_dwpub_new(cap: i64) -> *NxDwPub { 45 let raw: *u8 = sys_mmap(NX_DWPUB_BYTES) 46 let p: *NxDwPub = raw as *NxDwPub 47 p.cap = cap 48 p.buf = sys_mmap(cap) 49 p.len = 0 50 p.cu_start = 0 51 p.cu_open = 0 52 return p 53} 54 55func nx_dwpub_byte(p: *NxDwPub, b: i64) -> i64 { 56 if p.len >= p.cap { return -1 } 57 p.buf[p.len] = b & 0xFF 58 p.len = p.len + 1 59 return 0 60} 61 62func nx_dwpub_u16(p: *NxDwPub, v: i64) -> i64 { 63 nx_dwpub_byte(p, v & 0xFF) 64 return nx_dwpub_byte(p, (v >> 8) & 0xFF) 65} 66 67func nx_dwpub_u32(p: *NxDwPub, v: i64) -> i64 { 68 nx_dwpub_byte(p, v & 0xFF) 69 nx_dwpub_byte(p, (v >> 8) & 0xFF) 70 nx_dwpub_byte(p, (v >> 16) & 0xFF) 71 return nx_dwpub_byte(p, (v >> 24) & 0xFF) 72} 73 74func nx_dwpub_patch_u32(p: *NxDwPub, off: i64, v: i64) -> i64 { 75 p.buf[off] = v & 0xFF 76 p.buf[off + 1] = (v >> 8) & 0xFF 77 p.buf[off + 2] = (v >> 16) & 0xFF 78 p.buf[off + 3] = (v >> 24) & 0xFF 79 return 0 80} 81 82// Begin a new per-CU pubnames/pubtypes section. Caller passes the 83// CU's offset into .debug_info plus the length of that CU's 84// contribution. 85func nx_dwpub_cu_begin(p: *NxDwPub, info_off: i64, info_len: i64) -> i64 { 86 if p.cu_open == 1 { return -1 } 87 p.cu_start = p.len 88 p.cu_open = 1 89 nx_dwpub_u32(p, 0) // length placeholder 90 nx_dwpub_u16(p, NX_DWPUB_VERSION) 91 nx_dwpub_u32(p, info_off) 92 nx_dwpub_u32(p, info_len) 93 return 0 94} 95 96// Add one (DIE offset, name) tuple. `name` must be NUL-terminated. 97func nx_dwpub_add(p: *NxDwPub, die_off: i64, name: *u8) -> i64 { 98 if p.cu_open == 0 { return -1 } 99 nx_dwpub_u32(p, die_off) 100 var i: i64 = 0 101 while name[i] != 0 { 102 nx_dwpub_byte(p, name[i]) 103 i = i + 1 104 } 105 return nx_dwpub_byte(p, 0) 106} 107 108// Close the current CU's range list. Writes the 0 terminator 109// then patches the length field at the start of the unit. 110func nx_dwpub_cu_end(p: *NxDwPub) -> i64 { 111 if p.cu_open == 0 { return -1 } 112 nx_dwpub_u32(p, 0) 113 let unit_len: i64 = p.len - p.cu_start - 4 114 nx_dwpub_patch_u32(p, p.cu_start, unit_len) 115 p.cu_open = 0 116 return 0 117} 118 119// ---- self-test --------------------------------------------------- 120 121func main() -> i64 { 122 let p: *NxDwPub = nx_dwpub_new(256) 123 124 let main_name: *u8 = sys_mmap(8) 125 main_name[0] = 0x6D; main_name[1] = 0x61; main_name[2] = 0x69 126 main_name[3] = 0x6E; main_name[4] = 0 127 let foo_name: *u8 = sys_mmap(8) 128 foo_name[0] = 0x66; foo_name[1] = 0x6F; foo_name[2] = 0x6F; foo_name[3] = 0 129 130 nx_dwpub_cu_begin(p, 0x100, 0x500) 131 nx_dwpub_add(p, 0x40, main_name) 132 nx_dwpub_add(p, 0x80, foo_name) 133 nx_dwpub_cu_end(p) 134 135 // Length field at offset 0 = total - 4. 136 let stored_len: i64 = p.buf[0] 137 | (p.buf[1] << 8) 138 | (p.buf[2] << 16) 139 | (p.buf[3] << 24) 140 if stored_len != p.len - 4 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 141 142 // version u16 at offset 4 should be 2. 143 if p.buf[4] != 2 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 144 if p.buf[5] != 0 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 145 146 // info_off u32 at offset 6 should be 0x100. 147 if p.buf[6] != 0x00 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 148 if p.buf[7] != 0x01 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 149 150 // info_len u32 at offset 10 should be 0x500. 151 if p.buf[10] != 0x00 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 152 if p.buf[11] != 0x05 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 153 154 // First tuple at offset 14: die_off u32 = 0x40 then "main\0" 155 if p.buf[14] != 0x40 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 156 if p.buf[15] != 0 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 157 if p.buf[18] != 0x6D { return __syscall(93, 10, 0, 0, 0, 0, 0) } // 'm' 158 if p.buf[19] != 0x61 { return __syscall(93, 11, 0, 0, 0, 0, 0) } // 'a' 159 if p.buf[20] != 0x69 { return __syscall(93, 12, 0, 0, 0, 0, 0) } // 'i' 160 if p.buf[21] != 0x6E { return __syscall(93, 13, 0, 0, 0, 0, 0) } // 'n' 161 if p.buf[22] != 0 { return __syscall(93, 14, 0, 0, 0, 0, 0) } // NUL 162 163 // Total terminator should be the last 4 bytes = 0. 164 if p.buf[p.len - 1] != 0 { return __syscall(93, 15, 0, 0, 0, 0, 0) } 165 if p.buf[p.len - 4] != 0 { return __syscall(93, 16, 0, 0, 0, 0, 0) } 166 167 return 0 168}