nx_dwarf_abbrev.nx source
↩ module page · 221 lines · 8118 B
1// nx_dwarf_abbrev.nx -- DWARF .debug_abbrev section builder.
2//
3// DWARF DIE entries don't carry their own attribute schemas; they
4// reference an entry in .debug_abbrev that says "this DIE is tag T,
5// has children Y/N, and its attributes appear in the order
6// (name1, form1), (name2, form2), ...". Sharing one abbrev entry
7// across many DIEs makes .debug_info compact.
8//
9// One abbrev declaration encodes:
10// abbrev_code -- uleb128, the integer DIEs reference
11// tag -- uleb128 (DW_TAG_*)
12// has_children -- 1 byte: 0x00 = no, 0x01 = yes
13// sequence of (name, form) uleb128 pairs, terminated by (0, 0)
14//
15// The whole .debug_abbrev section is a concatenation of such
16// declarations, terminated by an abbrev_code of 0.
17//
18// We use nx_intern over (tag, has_children, attr_list_hash) to
19// dedup -- two DIEs with the same shape get the same abbrev code.
20//
21// Common DW_TAG_* / DW_AT_* / DW_FORM_* constants exposed for use
22// by the upcoming nx_dwarf_info.nx (DIE forest builder).
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
32// ---- selected DW_TAG_* (per DWARF v5 spec Section 7.5.4) ---------
33
34const NX_DW_TAG_compile_unit: i64 = 0x11
35const NX_DW_TAG_subprogram: i64 = 0x2E
36const NX_DW_TAG_variable: i64 = 0x34
37const NX_DW_TAG_formal_parameter: i64 = 0x05
38const NX_DW_TAG_lexical_block: i64 = 0x0B
39const NX_DW_TAG_base_type: i64 = 0x24
40const NX_DW_TAG_pointer_type: i64 = 0x0F
41const NX_DW_TAG_array_type: i64 = 0x01
42const NX_DW_TAG_structure_type: i64 = 0x13
43const NX_DW_TAG_member: i64 = 0x0D
44const NX_DW_TAG_enumeration_type: i64 = 0x04
45const NX_DW_TAG_enumerator: i64 = 0x28
46
47// ---- selected DW_AT_* ---------------------------------------------
48
49const NX_DW_AT_name: i64 = 0x03
50const NX_DW_AT_byte_size: i64 = 0x0B
51const NX_DW_AT_low_pc: i64 = 0x11
52const NX_DW_AT_high_pc: i64 = 0x12
53const NX_DW_AT_language: i64 = 0x13
54const NX_DW_AT_comp_dir: i64 = 0x1B
55const NX_DW_AT_producer: i64 = 0x25
56const NX_DW_AT_prototyped: i64 = 0x27
57const NX_DW_AT_stmt_list: i64 = 0x10
58const NX_DW_AT_type: i64 = 0x49
59const NX_DW_AT_encoding: i64 = 0x3E
60const NX_DW_AT_external: i64 = 0x3F
61const NX_DW_AT_decl_file: i64 = 0x3A
62const NX_DW_AT_decl_line: i64 = 0x3B
63
64// ---- selected DW_FORM_* -------------------------------------------
65
66const NX_DW_FORM_addr: i64 = 0x01
67const NX_DW_FORM_data1: i64 = 0x0B
68const NX_DW_FORM_data2: i64 = 0x05
69const NX_DW_FORM_data4: i64 = 0x06
70const NX_DW_FORM_data8: i64 = 0x07
71const NX_DW_FORM_string: i64 = 0x08
72const NX_DW_FORM_strp: i64 = 0x0E
73const NX_DW_FORM_ref4: i64 = 0x13
74const NX_DW_FORM_ref8: i64 = 0x14
75const NX_DW_FORM_sec_offset: i64 = 0x17
76const NX_DW_FORM_flag: i64 = 0x0C
77const NX_DW_FORM_flag_present: i64 = 0x19
78const NX_DW_FORM_udata: i64 = 0x0F
79const NX_DW_FORM_sdata: i64 = 0x0D
80
81// ---- abbreviation builder -----------------------------------------
82
83struct NxDwAbbrev {
84 buf: *u8,
85 cap: i64,
86 used: i64,
87 next_code: i64, // monotonically increasing abbrev_code
88 cur_open: i64, // 1 if a declaration is being built
89}
90
91const NX_DW_ABBREV_BYTES: i64 = 40
92
93func nx_dwabbrev_new(cap: i64) -> *NxDwAbbrev {
94 let raw: *u8 = sys_mmap(NX_DW_ABBREV_BYTES)
95 let a: *NxDwAbbrev = raw as *NxDwAbbrev
96 a.buf = sys_mmap(cap)
97 a.cap = cap
98 a.used = 0
99 a.next_code = 1 // codes are 1-based; 0 = sentinel
100 a.cur_open = 0
101 return a
102}
103
104// ---- byte append + uleb128 ---------------------------------------
105
106func nx_dwabbrev_byte(a: *NxDwAbbrev, v: i64) -> i64 {
107 if a.used >= a.cap { return -1 }
108 a.buf[a.used] = v & 0xFF
109 a.used = a.used + 1
110 return 0
111}
112
113func nx_dwabbrev_uleb(a: *NxDwAbbrev, v: i64) -> i64 {
114 var x: i64 = v
115 var go: i64 = 1
116 while go == 1 {
117 var b: i64 = x & 0x7F
118 x = x >> 7
119 if x != 0 { b = b | 0x80 }
120 if nx_dwabbrev_byte(a, b) < 0 { return -1 }
121 if x == 0 { go = 0 }
122 }
123 return 0
124}
125
126// ---- declaration API ---------------------------------------------
127
128// Begin a new abbreviation declaration. Returns the abbrev_code
129// the caller should reference from .debug_info DIEs.
130//
131// has_children: 0 (no) or 1 (yes).
132func nx_dwabbrev_begin(a: *NxDwAbbrev, tag: i64, has_children: i64) -> i64 {
133 let code: i64 = a.next_code
134 a.next_code = a.next_code + 1
135 nx_dwabbrev_uleb(a, code)
136 nx_dwabbrev_uleb(a, tag)
137 nx_dwabbrev_byte(a, has_children & 0x1)
138 a.cur_open = 1
139 return code
140}
141
142// Add an (attribute name, form) pair to the open declaration.
143func nx_dwabbrev_attr(a: *NxDwAbbrev, name: i64, form: i64) -> i64 {
144 if a.cur_open != 1 { return -1 }
145 nx_dwabbrev_uleb(a, name)
146 nx_dwabbrev_uleb(a, form)
147 return 0
148}
149
150// End the open declaration with the (0, 0) terminator pair.
151func nx_dwabbrev_end(a: *NxDwAbbrev) -> i64 {
152 if a.cur_open != 1 { return -1 }
153 nx_dwabbrev_byte(a, 0)
154 nx_dwabbrev_byte(a, 0)
155 a.cur_open = 0
156 return 0
157}
158
159// Finalize the section by appending the trailing 0 abbrev_code.
160// Caller does this before emitting the section bytes.
161func nx_dwabbrev_finish(a: *NxDwAbbrev) -> i64 {
162 if a.cur_open == 1 { nx_dwabbrev_end(a) }
163 nx_dwabbrev_byte(a, 0)
164 return 0
165}
166
167func nx_dwabbrev_size(a: *NxDwAbbrev) -> i64 { return a.used }
168func nx_dwabbrev_bytes(a: *NxDwAbbrev) -> *u8 { return a.buf }
169
170// ---- self-test ---------------------------------------------------
171
172func main() -> i64 {
173 let a: *NxDwAbbrev = nx_dwabbrev_new(256)
174
175 // Abbrev #1: a compile_unit DIE with name + producer + low_pc +
176 // high_pc + stmt_list (5 attributes), HAS children (subprograms).
177 let cu_code: i64 = nx_dwabbrev_begin(a, NX_DW_TAG_compile_unit, 1)
178 if cu_code != 1 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
179 nx_dwabbrev_attr(a, NX_DW_AT_name, NX_DW_FORM_strp)
180 nx_dwabbrev_attr(a, NX_DW_AT_producer, NX_DW_FORM_strp)
181 nx_dwabbrev_attr(a, NX_DW_AT_low_pc, NX_DW_FORM_addr)
182 nx_dwabbrev_attr(a, NX_DW_AT_high_pc, NX_DW_FORM_data8)
183 nx_dwabbrev_attr(a, NX_DW_AT_stmt_list, NX_DW_FORM_sec_offset)
184 nx_dwabbrev_end(a)
185
186 // Abbrev #2: a subprogram DIE -- name + low_pc + high_pc + type
187 // + external, no children.
188 let sub_code: i64 = nx_dwabbrev_begin(a, NX_DW_TAG_subprogram, 0)
189 if sub_code != 2 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
190 nx_dwabbrev_attr(a, NX_DW_AT_name, NX_DW_FORM_strp)
191 nx_dwabbrev_attr(a, NX_DW_AT_low_pc, NX_DW_FORM_addr)
192 nx_dwabbrev_attr(a, NX_DW_AT_high_pc, NX_DW_FORM_data8)
193 nx_dwabbrev_attr(a, NX_DW_AT_type, NX_DW_FORM_ref4)
194 nx_dwabbrev_attr(a, NX_DW_AT_external, NX_DW_FORM_flag_present)
195 nx_dwabbrev_end(a)
196
197 // Abbrev #3: a base_type DIE (e.g. int) -- name + byte_size +
198 // encoding, no children.
199 let bt_code: i64 = nx_dwabbrev_begin(a, NX_DW_TAG_base_type, 0)
200 if bt_code != 3 { return __syscall(93, 30, 0, 0, 0, 0, 0) }
201 nx_dwabbrev_attr(a, NX_DW_AT_name, NX_DW_FORM_strp)
202 nx_dwabbrev_attr(a, NX_DW_AT_byte_size, NX_DW_FORM_data1)
203 nx_dwabbrev_attr(a, NX_DW_AT_encoding, NX_DW_FORM_data1)
204 nx_dwabbrev_end(a)
205
206 // Finalize: trailing 0.
207 nx_dwabbrev_finish(a)
208
209 // Sanity check: section non-empty + last byte = 0.
210 if nx_dwabbrev_size(a) <= 0 { return __syscall(93, 40, 0, 0, 0, 0, 0) }
211 if a.buf[a.used - 1] != 0 { return __syscall(93, 41, 0, 0, 0, 0, 0) }
212
213 // First byte should be the abbrev_code 1 (uleb128 = 0x01).
214 if a.buf[0] != 1 { return __syscall(93, 50, 0, 0, 0, 0, 0) }
215 // Second byte: tag = NX_DW_TAG_compile_unit = 0x11.
216 if a.buf[1] != 0x11 { return __syscall(93, 51, 0, 0, 0, 0, 0) }
217 // Third byte: has_children = 1.
218 if a.buf[2] != 1 { return __syscall(93, 52, 0, 0, 0, 0, 0) }
219
220 return 0
221}