code wiki / (root) / nx_objdump.nx

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}