code wiki / (root) / nx_dwarf_addr.nx

nx_dwarf_addr.nx source

↩ module page · 179 lines · 5525 B

1// nx_dwarf_addr.nx -- DWARF v5 .debug_addr builder. 2// 3// Same indirection trick as .debug_str_offsets, applied to runtime 4// code addresses. DWARF v4 stored each address inline as 8 bytes 5// in DIE attributes (DW_FORM_addr). DWARF v5 introduces 6// DW_FORM_addrx -- a uleb128 index into a per-CU address table 7// living in .debug_addr. 8// 9// Benefits: 10// - All link-time-relocated addresses concentrate in ONE section 11// (the linker only patches .debug_addr, never DIEs). 12// - DIEs become smaller and more compressible. 13// - Addresses can be deduplicated within a CU. 14// 15// Per-CU layout (DWARF v5 7.27): 16// length u32 (excludes itself) 17// version u16 = 5 18// addr_size u8 = 8 19// segment_selector u8 = 0 20// addresses[] 8 bytes each (vaddr post-link) 21// 22// Caller emits one CU contribution per .debug_info CU and writes 23// one u64 per address the CU references. DIE attributes use form 24// DW_FORM_addrx (uleb128 index). 25// 26// Pairs with nx_dwarf_info (consumer) + nx_dwarf_str_offsets 27// (sister DWARF v5 indirection). 28 29// nx_safety_envelope: 30// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 31// sil_target: SIL1 32// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 33// verdict: NOT_YET_EVALUATED 34 35import "syscalls.nx" 36 37const NX_DWADDR_VERSION: i64 = 5 38const NX_DWADDR_ADDR_SIZE: i64 = 8 39const NX_DWADDR_SEG_SIZE: i64 = 0 40 41struct NxDwAddr { 42 buf: *u8, 43 len: i64, 44 cap: i64, 45 cu_start: i64, 46 cu_open: i64, 47} 48 49const NX_DWADDR_BYTES: i64 = 40 50 51func nx_dwaddr_new(cap: i64) -> *NxDwAddr { 52 let raw: *u8 = sys_mmap(NX_DWADDR_BYTES) 53 let a: *NxDwAddr = raw as *NxDwAddr 54 a.cap = cap 55 a.buf = sys_mmap(cap) 56 a.len = 0 57 a.cu_start = 0 58 a.cu_open = 0 59 return a 60} 61 62func nx_dwaddr_byte(a: *NxDwAddr, b: i64) -> i64 { 63 if a.len >= a.cap { return -1 } 64 a.buf[a.len] = b & 0xFF 65 a.len = a.len + 1 66 return 0 67} 68 69func nx_dwaddr_u16(a: *NxDwAddr, v: i64) -> i64 { 70 nx_dwaddr_byte(a, v & 0xFF) 71 return nx_dwaddr_byte(a, (v >> 8) & 0xFF) 72} 73 74func nx_dwaddr_u32(a: *NxDwAddr, v: i64) -> i64 { 75 nx_dwaddr_byte(a, v & 0xFF) 76 nx_dwaddr_byte(a, (v >> 8) & 0xFF) 77 nx_dwaddr_byte(a, (v >> 16) & 0xFF) 78 return nx_dwaddr_byte(a, (v >> 24) & 0xFF) 79} 80 81func nx_dwaddr_u64(a: *NxDwAddr, v: i64) -> i64 { 82 var i: i64 = 0 83 while i < 8 { 84 nx_dwaddr_byte(a, (v >> (i * 8)) & 0xFF) 85 i = i + 1 86 } 87 return 0 88} 89 90func nx_dwaddr_patch_u32(a: *NxDwAddr, off: i64, v: i64) -> i64 { 91 a.buf[off] = v & 0xFF 92 a.buf[off + 1] = (v >> 8) & 0xFF 93 a.buf[off + 2] = (v >> 16) & 0xFF 94 a.buf[off + 3] = (v >> 24) & 0xFF 95 return 0 96} 97 98// Begin a CU's address table. 99func nx_dwaddr_cu_begin(a: *NxDwAddr) -> i64 { 100 if a.cu_open == 1 { return -1 } 101 a.cu_start = a.len 102 a.cu_open = 1 103 nx_dwaddr_u32(a, 0) // length placeholder 104 nx_dwaddr_u16(a, NX_DWADDR_VERSION) 105 nx_dwaddr_byte(a, NX_DWADDR_ADDR_SIZE) 106 nx_dwaddr_byte(a, NX_DWADDR_SEG_SIZE) 107 return 0 108} 109 110// Append one address. Returns the index of the entry just written 111// (use as the addrx value in DIE attributes). 112func nx_dwaddr_add(a: *NxDwAddr, vaddr: i64) -> i64 { 113 if a.cu_open == 0 { return -1 } 114 let header_end: i64 = a.cu_start + 8 115 let idx: i64 = (a.len - header_end) / 8 116 nx_dwaddr_u64(a, vaddr) 117 return idx 118} 119 120// CU base = file offset of first address slot. Goes in 121// DW_AT_addr_base attribute. 122func nx_dwaddr_cu_base(a: *NxDwAddr) -> i64 { 123 if a.cu_open == 0 { return -1 } 124 return a.cu_start + 8 125} 126 127// Close a CU contribution. Patches the length field at the start. 128func nx_dwaddr_cu_end(a: *NxDwAddr) -> i64 { 129 if a.cu_open == 0 { return -1 } 130 let unit_len: i64 = a.len - a.cu_start - 4 131 nx_dwaddr_patch_u32(a, a.cu_start, unit_len) 132 a.cu_open = 0 133 return 0 134} 135 136// ---- self-test --------------------------------------------------- 137 138func main() -> i64 { 139 let a: *NxDwAddr = nx_dwaddr_new(128) 140 141 nx_dwaddr_cu_begin(a) 142 let base: i64 = nx_dwaddr_cu_base(a) 143 if base != 8 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 144 145 let i0: i64 = nx_dwaddr_add(a, 0x10000) 146 let i1: i64 = nx_dwaddr_add(a, 0x10100) 147 let i2: i64 = nx_dwaddr_add(a, 0x10200) 148 if i0 != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 149 if i1 != 1 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 150 if i2 != 2 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 151 152 nx_dwaddr_cu_end(a) 153 154 // Length field at offset 0 = total - 4. 155 let len: i64 = a.buf[0] 156 | (a.buf[1] << 8) 157 | (a.buf[2] << 16) 158 | (a.buf[3] << 24) 159 if len != a.len - 4 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 160 161 // version = 5. 162 if a.buf[4] != 5 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 163 164 // addr_size = 8 / seg_size = 0. 165 if a.buf[6] != 8 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 166 if a.buf[7] != 0 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 167 168 // First address at offset 8 = 0x10000 LE. 169 if a.buf[8] != 0x00 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 170 if a.buf[9] != 0x00 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 171 if a.buf[10] != 0x01 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 172 173 // Third address at offset 24 = 0x10200 LE. 174 if a.buf[24] != 0x00 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 175 if a.buf[25] != 0x02 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 176 if a.buf[26] != 0x01 { return __syscall(93, 14, 0, 0, 0, 0, 0) } 177 178 return 0 179}