code wiki / (root) / nx_dwarf_str_offsets.nx

nx_dwarf_str_offsets.nx source

↩ module page · 168 lines · 5399 B

1// nx_dwarf_str_offsets.nx -- DWARF v5 .debug_str_offsets builder. 2// 3// DWARF v5 introduced a level of indirection: instead of every DIE 4// attribute storing a direct file offset into .debug_str (form 5// DW_FORM_strp), per-CU strings are referenced by INDEX into a 6// per-CU offset table that lives in .debug_str_offsets (form 7// DW_FORM_strx). Benefits: 8// - Smaller DIEs in 32-bit DWARF (uleb128 index vs 4-byte offset). 9// - All string offsets concentrate in one section -- better 10// compression by gold/lld split-debug toolchains. 11// - Makes incremental linking cheaper (only patch the offset 12// table, not every DIE that referenced a moved string). 13// 14// Per-CU layout (DWARF v5 7.26): 15// length u32 (excludes itself) 16// version u16 = 5 17// padding u16 = 0 18// offsets[] u32 each (file offsets into .debug_str) 19// 20// Caller emits one CU contribution per .debug_info CU and writes 21// one u32 per string the CU references. DIE attributes use form 22// DW_FORM_strx (uleb128 index) which the consumer resolves via 23// (cu_str_offsets_base + idx*4) -> file offset into .debug_str. 24// 25// Pairs with nx_dwarf_str (string pool) + nx_dwarf_info (consumer 26// of strx forms). 27 28// nx_safety_envelope: 29// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 30// sil_target: SIL1 31// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 32// verdict: NOT_YET_EVALUATED 33 34import "syscalls.nx" 35 36const NX_DWSO_VERSION: i64 = 5 37 38struct NxDwSO { 39 buf: *u8, 40 len: i64, 41 cap: i64, 42 cu_start: i64, 43 cu_open: i64, 44} 45 46const NX_DWSO_BYTES: i64 = 40 47 48func nx_dwso_new(cap: i64) -> *NxDwSO { 49 let raw: *u8 = sys_mmap(NX_DWSO_BYTES) 50 let s: *NxDwSO = raw as *NxDwSO 51 s.cap = cap 52 s.buf = sys_mmap(cap) 53 s.len = 0 54 s.cu_start = 0 55 s.cu_open = 0 56 return s 57} 58 59func nx_dwso_byte(s: *NxDwSO, b: i64) -> i64 { 60 if s.len >= s.cap { return -1 } 61 s.buf[s.len] = b & 0xFF 62 s.len = s.len + 1 63 return 0 64} 65 66func nx_dwso_u16(s: *NxDwSO, v: i64) -> i64 { 67 nx_dwso_byte(s, v & 0xFF) 68 return nx_dwso_byte(s, (v >> 8) & 0xFF) 69} 70 71func nx_dwso_u32(s: *NxDwSO, v: i64) -> i64 { 72 nx_dwso_byte(s, v & 0xFF) 73 nx_dwso_byte(s, (v >> 8) & 0xFF) 74 nx_dwso_byte(s, (v >> 16) & 0xFF) 75 return nx_dwso_byte(s, (v >> 24) & 0xFF) 76} 77 78func nx_dwso_patch_u32(s: *NxDwSO, off: i64, v: i64) -> i64 { 79 s.buf[off] = v & 0xFF 80 s.buf[off + 1] = (v >> 8) & 0xFF 81 s.buf[off + 2] = (v >> 16) & 0xFF 82 s.buf[off + 3] = (v >> 24) & 0xFF 83 return 0 84} 85 86// Begin a CU's offset table. 87func nx_dwso_cu_begin(s: *NxDwSO) -> i64 { 88 if s.cu_open == 1 { return -1 } 89 s.cu_start = s.len 90 s.cu_open = 1 91 nx_dwso_u32(s, 0) // length placeholder 92 nx_dwso_u16(s, NX_DWSO_VERSION) 93 nx_dwso_u16(s, 0) // padding 94 return 0 95} 96 97// Append one string offset (file offset into .debug_str). Returns 98// the index of the entry just written -- caller stores this and 99// uses it as the strx in DIE attributes. 100func nx_dwso_add(s: *NxDwSO, str_offset: i64) -> i64 { 101 if s.cu_open == 0 { return -1 } 102 let header_end: i64 = s.cu_start + 8 103 let idx: i64 = (s.len - header_end) / 4 104 nx_dwso_u32(s, str_offset) 105 return idx 106} 107 108// The "base" address of this CU's offset table -- the offset where 109// the first u32 entry lives. Goes into the CU's 110// DW_AT_str_offsets_base attribute in .debug_info. 111func nx_dwso_cu_base(s: *NxDwSO) -> i64 { 112 if s.cu_open == 0 { return -1 } 113 return s.cu_start + 8 114} 115 116// Close a CU contribution. Patches the length field at the start. 117func nx_dwso_cu_end(s: *NxDwSO) -> i64 { 118 if s.cu_open == 0 { return -1 } 119 let unit_len: i64 = s.len - s.cu_start - 4 120 nx_dwso_patch_u32(s, s.cu_start, unit_len) 121 s.cu_open = 0 122 return 0 123} 124 125// ---- self-test --------------------------------------------------- 126 127func main() -> i64 { 128 let s: *NxDwSO = nx_dwso_new(128) 129 130 nx_dwso_cu_begin(s) 131 let base: i64 = nx_dwso_cu_base(s) 132 if base != 8 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 133 134 // Add three strings. 135 let i0: i64 = nx_dwso_add(s, 0x100) 136 let i1: i64 = nx_dwso_add(s, 0x108) 137 let i2: i64 = nx_dwso_add(s, 0x110) 138 if i0 != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 139 if i1 != 1 { return __syscall(93, 3, 0, 0, 0, 0, 0) } 140 if i2 != 2 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 141 142 nx_dwso_cu_end(s) 143 144 // Length field at offset 0 = total - 4. 145 let len: i64 = s.buf[0] 146 | (s.buf[1] << 8) 147 | (s.buf[2] << 16) 148 | (s.buf[3] << 24) 149 if len != s.len - 4 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 150 151 // version u16 at offset 4 = 5. 152 if s.buf[4] != 5 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 153 if s.buf[5] != 0 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 154 155 // padding u16 at offset 6 = 0. 156 if s.buf[6] != 0 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 157 if s.buf[7] != 0 { return __syscall(93, 9, 0, 0, 0, 0, 0) } 158 159 // First entry at offset 8 = 0x100 LE. 160 if s.buf[8] != 0x00 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 161 if s.buf[9] != 0x01 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 162 163 // Third entry at offset 16 = 0x110 LE. 164 if s.buf[16] != 0x10 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 165 if s.buf[17] != 0x01 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 166 167 return 0 168}