code wiki / (root) / nx_dwarf_loclists.nx

nx_dwarf_loclists.nx source

↩ module page · 247 lines · 7363 B

1// nx_dwarf_loclists.nx -- DWARF v5 .debug_loclists builder. 2// 3// Companion to nx_dwarf_loc (single location expression at a fixed 4// PC). loclists describes a variable's location as a SEQUENCE of 5// (PC range, location expression) pairs -- needed when an 6// optimizer moves the variable across registers / spills it to 7// the frame at different points in the function. 8// 9// Without loclists, gdb shows the variable's last-known location 10// at every breakpoint, which is wrong inside the optimized 11// function body. With loclists, gdb shows '<optimized out>' 12// outside any covered range and the right register/offset inside. 13// 14// Per-CU layout (DWARF v5 7.7.3) -- mirrors nx_dwarf_rnglists: 15// length u32 16// version u16 = 5 17// addr_size u8 = 8 18// segment_selector u8 = 0 19// offset_entry_count u32 20// offsets[count] u32 21// loc lists sequence of DW_LLE_* opcodes 22// 23// DW_LLE_* opcodes (subset we emit): 24// end_of_list 0x00 25// base_addressx 0x01 26// startx_endx 0x02 27// startx_length 0x03 28// offset_pair 0x04 29// default_location 0x05 30// base_address 0x06 31// start_end 0x07 32// start_length 0x08 33// 34// Each entry that includes a "location description" is followed 35// by a uleb128 length and that many bytes of DW_OP_* expression 36// (built via nx_dwarf_loc). 37// 38// Pairs with nx_dwarf_loc (location-expression bytes) + 39// nx_dwarf_info (DIE attributes referencing loclists offsets) + 40// nx_dwarf_rnglists (sister section). 41 42// nx_safety_envelope: 43// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 44// sil_target: SIL1 45// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 46// verdict: NOT_YET_EVALUATED 47 48import "syscalls.nx" 49 50const NX_DWLL_VERSION: i64 = 5 51const NX_DWLL_ADDR_SIZE: i64 = 8 52const NX_DWLL_SEG_SIZE: i64 = 0 53 54// DW_LLE_* 55const NX_DW_LLE_END_OF_LIST: i64 = 0x00 56const NX_DW_LLE_BASE_ADDRESSX: i64 = 0x01 57const NX_DW_LLE_STARTX_ENDX: i64 = 0x02 58const NX_DW_LLE_STARTX_LENGTH: i64 = 0x03 59const NX_DW_LLE_OFFSET_PAIR: i64 = 0x04 60const NX_DW_LLE_DEFAULT_LOC: i64 = 0x05 61const NX_DW_LLE_BASE_ADDRESS: i64 = 0x06 62const NX_DW_LLE_START_END: i64 = 0x07 63const NX_DW_LLE_START_LENGTH: i64 = 0x08 64 65struct NxDwLl { 66 buf: *u8, 67 len: i64, 68 cap: i64, 69 cu_start: i64, 70 cu_open: i64, 71} 72 73const NX_DWLL_BYTES: i64 = 40 74 75func nx_dwll_new(cap: i64) -> *NxDwLl { 76 let raw: *u8 = sys_mmap(NX_DWLL_BYTES) 77 let r: *NxDwLl = raw as *NxDwLl 78 r.cap = cap 79 r.buf = sys_mmap(cap) 80 r.len = 0 81 r.cu_start = 0 82 r.cu_open = 0 83 return r 84} 85 86func nx_dwll_byte(r: *NxDwLl, b: i64) -> i64 { 87 if r.len >= r.cap { return -1 } 88 r.buf[r.len] = b & 0xFF 89 r.len = r.len + 1 90 return 0 91} 92 93func nx_dwll_u16(r: *NxDwLl, v: i64) -> i64 { 94 nx_dwll_byte(r, v & 0xFF) 95 return nx_dwll_byte(r, (v >> 8) & 0xFF) 96} 97 98func nx_dwll_u32(r: *NxDwLl, v: i64) -> i64 { 99 nx_dwll_byte(r, v & 0xFF) 100 nx_dwll_byte(r, (v >> 8) & 0xFF) 101 nx_dwll_byte(r, (v >> 16) & 0xFF) 102 return nx_dwll_byte(r, (v >> 24) & 0xFF) 103} 104 105func nx_dwll_u64(r: *NxDwLl, v: i64) -> i64 { 106 var i: i64 = 0 107 while i < 8 { 108 nx_dwll_byte(r, (v >> (i * 8)) & 0xFF) 109 i = i + 1 110 } 111 return 0 112} 113 114func nx_dwll_uleb(r: *NxDwLl, v: i64) -> i64 { 115 var n: i64 = v 116 var done: i64 = 0 117 while done == 0 { 118 let lo: i64 = n & 0x7F 119 n = (n >> 7) & 0x01FFFFFFFFFFFFFF 120 if n == 0 { 121 nx_dwll_byte(r, lo) 122 done = 1 123 } else { 124 nx_dwll_byte(r, lo | 0x80) 125 } 126 } 127 return 0 128} 129 130func nx_dwll_patch_u32(r: *NxDwLl, off: i64, v: i64) -> i64 { 131 r.buf[off] = v & 0xFF 132 r.buf[off + 1] = (v >> 8) & 0xFF 133 r.buf[off + 2] = (v >> 16) & 0xFF 134 r.buf[off + 3] = (v >> 24) & 0xFF 135 return 0 136} 137 138// Begin a CU contribution. `n_offset_entries` reserves space for 139// the offset table (one u32 per loc-list). 140func nx_dwll_cu_begin(r: *NxDwLl, n_offset_entries: i64) -> i64 { 141 if r.cu_open == 1 { return -1 } 142 r.cu_start = r.len 143 r.cu_open = 1 144 nx_dwll_u32(r, 0) // length placeholder 145 nx_dwll_u16(r, NX_DWLL_VERSION) 146 nx_dwll_byte(r, NX_DWLL_ADDR_SIZE) 147 nx_dwll_byte(r, NX_DWLL_SEG_SIZE) 148 nx_dwll_u32(r, n_offset_entries) 149 var i: i64 = 0 150 while i < n_offset_entries { 151 nx_dwll_u32(r, 0) 152 i = i + 1 153 } 154 return 0 155} 156 157func nx_dwll_patch_offset(r: *NxDwLl, idx: i64, list_off_in_cu: i64) -> i64 { 158 let off_in_buf: i64 = r.cu_start + 12 + idx * 4 159 return nx_dwll_patch_u32(r, off_in_buf, list_off_in_cu) 160} 161 162func nx_dwll_list_offset(r: *NxDwLl, n_offset_entries: i64) -> i64 { 163 let header_end: i64 = r.cu_start + 12 + n_offset_entries * 4 164 return r.len - header_end 165} 166 167// Emit a (start, length) entry followed by a location description. 168// `expr_buf` is `expr_len` bytes from nx_dwarf_loc. 169func nx_dwll_start_length(r: *NxDwLl, start: i64, length: i64, expr_buf: *u8, expr_len: i64) -> i64 { 170 nx_dwll_byte(r, NX_DW_LLE_START_LENGTH) 171 nx_dwll_u64(r, start) 172 nx_dwll_uleb(r, length) 173 nx_dwll_uleb(r, expr_len) 174 var i: i64 = 0 175 while i < expr_len { 176 nx_dwll_byte(r, expr_buf[i]) 177 i = i + 1 178 } 179 return 0 180} 181 182// Default location (always-applies fallback). 183func nx_dwll_default_location(r: *NxDwLl, expr_buf: *u8, expr_len: i64) -> i64 { 184 nx_dwll_byte(r, NX_DW_LLE_DEFAULT_LOC) 185 nx_dwll_uleb(r, expr_len) 186 var i: i64 = 0 187 while i < expr_len { 188 nx_dwll_byte(r, expr_buf[i]) 189 i = i + 1 190 } 191 return 0 192} 193 194func nx_dwll_end_list(r: *NxDwLl) -> i64 { 195 return nx_dwll_byte(r, NX_DW_LLE_END_OF_LIST) 196} 197 198func nx_dwll_cu_end(r: *NxDwLl) -> i64 { 199 if r.cu_open == 0 { return -1 } 200 let unit_len: i64 = r.len - r.cu_start - 4 201 nx_dwll_patch_u32(r, r.cu_start, unit_len) 202 r.cu_open = 0 203 return 0 204} 205 206// ---- self-test --------------------------------------------------- 207 208func main() -> i64 { 209 let r: *NxDwLl = nx_dwll_new(256) 210 211 // Synthesise a tiny DW_OP_reg10 expression (one byte: 0x5A). 212 let expr: *u8 = sys_mmap(8) 213 expr[0] = 0x5A // DW_OP_reg10 214 215 nx_dwll_cu_begin(r, 1) 216 let off0: i64 = nx_dwll_list_offset(r, 1) 217 nx_dwll_patch_offset(r, 0, off0) 218 nx_dwll_start_length(r, 0x10000, 0x80, expr, 1) 219 nx_dwll_end_list(r) 220 nx_dwll_cu_end(r) 221 222 // Length field at offset 0 = total - 4. 223 let len: i64 = r.buf[0] 224 | (r.buf[1] << 8) 225 | (r.buf[2] << 16) 226 | (r.buf[3] << 24) 227 if len != r.len - 4 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 228 229 // version = 5. 230 if r.buf[4] != 5 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 231 232 // addr_size = 8 / seg_size = 0. 233 if r.buf[6] != 8 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 234 if r.buf[7] != 0 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 235 236 // n_offset_entries = 1. 237 if r.buf[8] != 1 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 238 239 // First offset entry (offset 12) should be 0. 240 if r.buf[12] != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 241 242 // First list opcode at offset 16 (header_end = 12 + 4 = 16): 243 // should be NX_DW_LLE_START_LENGTH = 0x08. 244 if r.buf[16] != 0x08 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 245 246 return 0 247}