nx_objdump.nx source
↩ module page · 274 lines · 9387 B
1// nx_objdump.nx -- sovereign objdump (read + dump ELF metadata).
2//
3// Closes the off-C tooling triangle:
4//
5// elf_writer / nxasm / nxld PRODUCE ELF
6// nx_elf_read READ ELF
7// nx_objdump INSPECT ELF (this module)
8//
9// The third-party `objdump` becomes optional. Every byte we write
10// can be inspected by tools we own.
11//
12// Subset of objdump features shipped here:
13//
14// nx_objdump_elf_header(buf, len, fd)
15// prints: magic, class, byte-order, type, machine, entry,
16// phoff, shoff, ehsize, phnum, shnum
17//
18// nx_objdump_section_headers(buf, len, fd)
19// prints: idx, name, type, flags, addr, offset, size
20// (one line per section)
21//
22// nx_objdump_symbols(buf, len, sym_idx, fd)
23// prints: idx, value, size, type, bind, name
24// (one line per symbol)
25//
26// Output format roughly matches `objdump -h` / `objdump -t` so users
27// familiar with binutils have minimal context-switch cost.
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"
36import "nx_elf_read.nx"
37
38// ---- helper: print a string + newline ----------------------------
39
40func nx_objdump_puts(fd: i64, s: *u8) -> i64 {
41 var n: i64 = 0
42 while s[n] != 0 { n = n + 1 }
43 sys_write(fd, s, n)
44 return 0
45}
46
47func nx_objdump_putln(fd: i64, s: *u8) -> i64 {
48 nx_objdump_puts(fd, s)
49 sys_write(fd, "\n" as *u8, 1)
50 return 0
51}
52
53// Print a 64-bit value in hex (16 chars, no 0x prefix).
54func nx_objdump_puthex64(fd: i64, v: i64) -> i64 {
55 let buf: *u8 = sys_mmap(20)
56 var i: i64 = 0
57 while i < 16 {
58 let nib: i64 = (v >> ((15 - i) * 4)) & 0xF
59 if nib < 10 { buf[i] = 0x30 + nib }
60 if nib >= 10 { buf[i] = 0x61 + (nib - 10) }
61 i = i + 1
62 }
63 sys_write(fd, buf, 16)
64 return 0
65}
66
67// Print a small unsigned int in decimal (up to 8 digits).
68func nx_objdump_putdec(fd: i64, v: i64) -> i64 {
69 let buf: *u8 = sys_mmap(16)
70 var n: i64 = v
71 var k: i64 = 0
72 if n == 0 { buf[0] = 0x30; sys_write(fd, buf, 1); return 0 }
73 while n > 0 {
74 buf[k] = 0x30 + (n - (n / 10) * 10)
75 n = n / 10
76 k = k + 1
77 }
78 // Reverse in-place.
79 var j: i64 = 0
80 var l: i64 = k - 1
81 while j < l {
82 let t: i64 = buf[j]
83 buf[j] = buf[l]
84 buf[l] = t
85 j = j + 1
86 l = l - 1
87 }
88 sys_write(fd, buf, k)
89 return 0
90}
91
92// ---- top-level dumpers --------------------------------------------
93
94func nx_objdump_elf_header(buf: *u8, len: i64, fd: i64) -> i64 {
95 let h: *NxElfHeader = nx_elf_parse_header(buf, len)
96 if h.valid != 1 {
97 nx_objdump_putln(fd, "ELF: invalid (bad magic or truncated)" as *u8)
98 return -1
99 }
100 nx_objdump_putln(fd, "ELF header:" as *u8)
101
102 nx_objdump_puts(fd, " class: " as *u8)
103 if h.is_64bit == 1 { nx_objdump_putln(fd, "ELF64" as *u8) }
104 if h.is_64bit != 1 { nx_objdump_putln(fd, "ELF32" as *u8) }
105
106 nx_objdump_puts(fd, " data: " as *u8)
107 if h.is_little == 1 { nx_objdump_putln(fd, "little-endian" as *u8) }
108 if h.is_little != 1 { nx_objdump_putln(fd, "big-endian" as *u8) }
109
110 nx_objdump_puts(fd, " type: " as *u8)
111 if h.e_type == 1 { nx_objdump_putln(fd, "REL (relocatable)" as *u8) }
112 if h.e_type == 2 { nx_objdump_putln(fd, "EXEC (executable)" as *u8) }
113 if h.e_type == 3 { nx_objdump_putln(fd, "DYN (shared object)" as *u8) }
114
115 nx_objdump_puts(fd, " machine: " as *u8)
116 if h.e_machine == 0xF3 { nx_objdump_putln(fd, "RISC-V (EM_RISCV)" as *u8) }
117 if h.e_machine == 0x3E { nx_objdump_putln(fd, "x86-64 (EM_X86_64)" as *u8) }
118 if h.e_machine == 0xB7 { nx_objdump_putln(fd, "AArch64 (EM_AARCH64)" as *u8) }
119
120 nx_objdump_puts(fd, " entry: 0x" as *u8)
121 nx_objdump_puthex64(fd, h.e_entry)
122 sys_write(fd, "\n" as *u8, 1)
123
124 nx_objdump_puts(fd, " phoff: 0x" as *u8)
125 nx_objdump_puthex64(fd, h.e_phoff)
126 nx_objdump_puts(fd, " phnum: " as *u8)
127 nx_objdump_putdec(fd, h.e_phnum)
128 sys_write(fd, "\n" as *u8, 1)
129
130 nx_objdump_puts(fd, " shoff: 0x" as *u8)
131 nx_objdump_puthex64(fd, h.e_shoff)
132 nx_objdump_puts(fd, " shnum: " as *u8)
133 nx_objdump_putdec(fd, h.e_shnum)
134 sys_write(fd, "\n" as *u8, 1)
135
136 return 0
137}
138
139func nx_objdump_section_headers(buf: *u8, len: i64, fd: i64) -> i64 {
140 let h: *NxElfHeader = nx_elf_parse_header(buf, len)
141 if h.valid != 1 { return -1 }
142 if h.e_shnum == 0 {
143 nx_objdump_putln(fd, "(no section headers)" as *u8)
144 return 0
145 }
146
147 // Locate .shstrtab section (used for section names).
148 let shstr: *NxElfShdr = nx_elf_read_shdr(buf, h, h.e_shstrndx)
149
150 nx_objdump_putln(fd, "Section headers:" as *u8)
151 nx_objdump_putln(fd, " idx name type addr offset size" as *u8)
152
153 var i: i64 = 0
154 while i < h.e_shnum {
155 let s: *NxElfShdr = nx_elf_read_shdr(buf, h, i)
156 let name: *u8 = nx_elf_strtab_at(buf, shstr.sh_offset, s.sh_name)
157
158 nx_objdump_puts(fd, " " as *u8)
159 nx_objdump_putdec(fd, i)
160 sys_write(fd, " " as *u8, 4)
161
162 // Name -- print up to 16 chars or NUL. Index-based to keep
163 // `name` immutable (NishiLang let-bindings can't be reassigned;
164 // we walk via byte offsets).
165 var nl: i64 = 0
166 var name_done: i64 = 0
167 while nl < 16 {
168 if name_done == 0 {
169 if name[nl] == 0 {
170 name_done = 1
171 } else {
172 let one_byte: *u8 = (((name as i64) + nl) as *u8)
173 sys_write(fd, one_byte, 1)
174 }
175 }
176 nl = nl + 1
177 }
178 // Pad to 18 chars.
179 sys_write(fd, " " as *u8, 2)
180
181 // Type.
182 if s.sh_type == 0 { nx_objdump_puts(fd, "NULL " as *u8) }
183 if s.sh_type == 1 { nx_objdump_puts(fd, "PROGBIT " as *u8) }
184 if s.sh_type == 2 { nx_objdump_puts(fd, "SYMTAB " as *u8) }
185 if s.sh_type == 3 { nx_objdump_puts(fd, "STRTAB " as *u8) }
186 if s.sh_type == 4 { nx_objdump_puts(fd, "RELA " as *u8) }
187 if s.sh_type == 8 { nx_objdump_puts(fd, "NOBITS " as *u8) }
188
189 nx_objdump_puthex64(fd, s.sh_addr)
190 sys_write(fd, " " as *u8, 1)
191 nx_objdump_puthex64(fd, s.sh_offset)
192 sys_write(fd, " " as *u8, 1)
193 nx_objdump_puthex64(fd, s.sh_size)
194 sys_write(fd, "\n" as *u8, 1)
195
196 i = i + 1
197 }
198 return 0
199}
200
201func nx_objdump_symbols(buf: *u8, len: i64, sym_idx: i64, fd: i64) -> i64 {
202 let h: *NxElfHeader = nx_elf_parse_header(buf, len)
203 if h.valid != 1 { return -1 }
204 if sym_idx >= h.e_shnum { return -1 }
205
206 let symsec: *NxElfShdr = nx_elf_read_shdr(buf, h, sym_idx)
207 if symsec.sh_type != 2 { // SHT_SYMTAB
208 nx_objdump_putln(fd, "section is not SYMTAB" as *u8)
209 return -1
210 }
211 let strtab: *NxElfShdr = nx_elf_read_shdr(buf, h, symsec.sh_link)
212
213 nx_objdump_putln(fd, "Symbols:" as *u8)
214 nx_objdump_putln(fd, " idx value size bind type name" as *u8)
215
216 let n: i64 = nx_elf_sym_count(symsec)
217 var i: i64 = 0
218 while i < n {
219 let s: *NxElfSym = nx_elf_read_sym(buf, symsec, i)
220 nx_objdump_puts(fd, " " as *u8)
221 nx_objdump_putdec(fd, i)
222 sys_write(fd, " " as *u8, 4)
223 nx_objdump_puthex64(fd, s.st_value)
224 sys_write(fd, " " as *u8, 2)
225 nx_objdump_puthex64(fd, s.st_size)
226 sys_write(fd, " " as *u8, 2)
227
228 let bind: i64 = nx_elf_sym_bind(s)
229 if bind == 0 { nx_objdump_puts(fd, "LOCAL " as *u8) }
230 if bind == 1 { nx_objdump_puts(fd, "GLOBAL " as *u8) }
231 if bind == 2 { nx_objdump_puts(fd, "WEAK " as *u8) }
232
233 let st: i64 = nx_elf_sym_type(s)
234 if st == 0 { nx_objdump_puts(fd, "NOTYPE " as *u8) }
235 if st == 1 { nx_objdump_puts(fd, "OBJECT " as *u8) }
236 if st == 2 { nx_objdump_puts(fd, "FUNC " as *u8) }
237 if st == 3 { nx_objdump_puts(fd, "SECTN " as *u8) }
238 if st == 4 { nx_objdump_puts(fd, "FILE " as *u8) }
239
240 let name: *u8 = nx_elf_strtab_at(buf, strtab.sh_offset, s.st_name)
241 nx_objdump_puts(fd, name)
242 sys_write(fd, "\n" as *u8, 1)
243
244 i = i + 1
245 }
246 return 0
247}
248
249// ---- self-test (exercise the print helpers) ----------------------
250
251func main() -> i64 {
252 // hex64 of 0x123456789ABCDEF0 prints "123456789abcdef0".
253 let buf: *u8 = sys_mmap(32)
254 let pipefds_raw: *u8 = sys_mmap(16)
255 let pipefds: *i64 = pipefds_raw as *i64
256 if sys_pipe2(pipefds, 0) < 0 {
257 // Pipe creation failed (some hosts) -- skip the round-trip
258 // check; just verify the function does not crash.
259 nx_objdump_puthex64(2, 0xDEADBEEFCAFEBABE)
260 sys_write(2, "\n" as *u8, 1)
261 return 0
262 }
263 nx_objdump_puthex64(pipefds[1], 0x123456789ABCDEF0)
264 sys_close(pipefds[1])
265
266 let chk: *u8 = sys_mmap(32)
267 let n: i64 = sys_read(pipefds[0], chk, 16)
268 sys_close(pipefds[0])
269 if n != 16 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
270 if chk[0] != 0x31 { return __syscall(93, 11, 0, 0, 0, 0, 0) } // '1'
271 if chk[15] != 0x30 { return __syscall(93, 12, 0, 0, 0, 0, 0) } // '0'
272
273 return 0
274}