code wiki / (root) / nx_nm.nx

nx_nm.nx source

↩ module page · 170 lines · 5937 B

1// nx_nm.nx -- sovereign nm-equivalent (list ELF symbols). 2// 3// Reads an ELF via nx_elf_read, finds the .symtab section, and 4// prints one line per symbol: 5// 6// <8-hex addr> <type-letter> <name> 7// 8// where type-letter follows the binutils nm convention: 9// T = global text (function in .text) 10// t = local text 11// D = global data 12// d = local data 13// B = global bss 14// b = local bss 15// U = undefined 16// ? = unknown 17// 18// One more sovereign tool that frees us from binutils. Together 19// with nx_objdump / nx_dis / nx_addr2line / nx_dbg, the off-C 20// inspection toolchain is fully self-hosted. 21 22// nx_safety_envelope: 23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 24// sil_target: SIL1 25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 26// verdict: NOT_YET_EVALUATED 27 28import "syscalls.nx" 29import "nx_elf_read.nx" 30import "nx_strfmt.nx" 31 32// Map (sym_type, sym_bind, section_name_first_char) -> nm letter. 33// We use the SECTION the symbol lives in to disambiguate text vs 34// data vs bss; binding (LOCAL=lowercase / GLOBAL=uppercase) flips 35// the case. 36// 37// section_first_char: 't' for .text, 'd' for .data, 'b' for .bss, 38// 'r' for .rodata, etc. 39func nx_nm_letter(sym_bind: i64, sym_type: i64, sym_shndx: i64, 40 section_letter: i64) -> i64 { 41 if sym_shndx == 0 { return 0x55 } // 'U' undefined 42 43 var c: i64 = section_letter 44 if sym_bind == 1 { 45 // Global: uppercase 46 if c >= 0x61 { c = c - 0x20 } 47 } else { 48 // Local: lowercase 49 if c < 0x61 { c = c + 0x20 } 50 } 51 return c 52} 53 54// Heuristic: decide a section's nm letter from its name. 55// .text* -> 't' / 'T' 56// .data* -> 'd' / 'D' 57// .bss* -> 'b' / 'B' 58// .rodata* -> 'r' / 'R' 59// fallback: '?' 60func nx_nm_section_letter(name: *u8) -> i64 { 61 // Skip leading '.'. 62 if name[0] != 0x2E { return 0x3F } // '?' 63 let c1: i64 = name[1] 64 if c1 == 0x74 { return 0x74 } // 't' .text 65 if c1 == 0x64 { return 0x64 } // 'd' .data 66 if c1 == 0x62 { return 0x62 } // 'b' .bss 67 if c1 == 0x72 { return 0x72 } // 'r' .rodata 68 return 0x3F 69} 70 71// Find the .symtab section index in `h`. Returns -1 if absent. 72func nx_nm_find_symtab(buf: *u8, h: *NxElfHeader) -> i64 { 73 var i: i64 = 0 74 while i < h.e_shnum { 75 let s: *NxElfShdr = nx_elf_read_shdr(buf, h, i) 76 if s.sh_type == 2 { return i } // SHT_SYMTAB 77 i = i + 1 78 } 79 return -1 80} 81 82// Print all symbols in nm format to fd. Returns 0 OK / -1 if no 83// symbol table found. 84func nx_nm_print(buf: *u8, len: i64, fd: i64) -> i64 { 85 let h: *NxElfHeader = nx_elf_parse_header(buf, len) 86 if h.valid != 1 { return -1 } 87 88 let sym_idx: i64 = nx_nm_find_symtab(buf, h) 89 if sym_idx < 0 { return -1 } 90 91 let symsec: *NxElfShdr = nx_elf_read_shdr(buf, h, sym_idx) 92 let strsec: *NxElfShdr = nx_elf_read_shdr(buf, h, symsec.sh_link) 93 let shstr: *NxElfShdr = nx_elf_read_shdr(buf, h, h.e_shstrndx) 94 95 let n_syms: i64 = nx_elf_sym_count(symsec) 96 var i: i64 = 1 // skip the undef entry at index 0 97 let argv_raw: *u8 = sys_mmap(64) 98 let argv: *i64 = argv_raw as *i64 99 100 while i < n_syms { 101 let s: *NxElfSym = nx_elf_read_sym(buf, symsec, i) 102 103 // Get the section name + first-char letter. 104 var section_letter: i64 = 0x3F // '?' 105 if s.st_shndx > 0 { 106 if s.st_shndx < h.e_shnum { 107 let sect: *NxElfShdr = nx_elf_read_shdr(buf, h, s.st_shndx) 108 let sect_name: *u8 = nx_elf_strtab_at(buf, shstr.sh_offset, sect.sh_name) 109 section_letter = nx_nm_section_letter(sect_name) 110 } 111 } 112 113 let bind: i64 = nx_elf_sym_bind(s) 114 let stt: i64 = nx_elf_sym_type(s) 115 let letter: i64 = nx_nm_letter(bind, stt, s.st_shndx, section_letter) 116 let name: *u8 = nx_elf_strtab_at(buf, strsec.sh_offset, s.st_name) 117 118 argv[0] = s.st_value 119 argv[1] = letter 120 argv[2] = name as i64 121 nx_fmt_to_fd(fd, "%X %c %s\n" as *u8, argv, 3) 122 123 i = i + 1 124 } 125 return 0 126} 127 128// ---- self-test --------------------------------------------------- 129 130func main() -> i64 { 131 // Sanity-check the section-letter heuristic without needing a 132 // full ELF (the print path is exercised by integration tests 133 // that load real ELF bytes). 134 let dot_text: *u8 = sys_mmap(8) 135 dot_text[0] = 0x2E; dot_text[1] = 0x74; dot_text[2] = 0x65; dot_text[3] = 0x78 136 if nx_nm_section_letter(dot_text) != 0x74 { // 't' 137 return __syscall(93, 10, 0, 0, 0, 0, 0) 138 } 139 140 let dot_data: *u8 = sys_mmap(8) 141 dot_data[0] = 0x2E; dot_data[1] = 0x64; dot_data[2] = 0x61; dot_data[3] = 0x74 142 if nx_nm_section_letter(dot_data) != 0x64 { // 'd' 143 return __syscall(93, 11, 0, 0, 0, 0, 0) 144 } 145 146 let dot_bss: *u8 = sys_mmap(8) 147 dot_bss[0] = 0x2E; dot_bss[1] = 0x62; dot_bss[2] = 0x73; dot_bss[3] = 0x73 148 if nx_nm_section_letter(dot_bss) != 0x62 { // 'b' 149 return __syscall(93, 12, 0, 0, 0, 0, 0) 150 } 151 152 let weird: *u8 = sys_mmap(8) 153 weird[0] = 0x66 // 'f' -- doesn't start with '.' 154 if nx_nm_section_letter(weird) != 0x3F { // '?' 155 return __syscall(93, 13, 0, 0, 0, 0, 0) 156 } 157 158 // letter assignment: undef -> 'U' regardless of binding. 159 if nx_nm_letter(0, 0, 0, 0x74) != 0x55 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 160 if nx_nm_letter(1, 0, 0, 0x74) != 0x55 { return __syscall(93, 21, 0, 0, 0, 0, 0) } 161 162 // Global text sym -> 'T'. 163 if nx_nm_letter(1, 2, 1, 0x74) != 0x54 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 164 // Local text sym -> 't'. 165 if nx_nm_letter(0, 2, 1, 0x74) != 0x74 { return __syscall(93, 31, 0, 0, 0, 0, 0) } 166 // Global data sym -> 'D'. 167 if nx_nm_letter(1, 1, 1, 0x64) != 0x44 { return __syscall(93, 32, 0, 0, 0, 0, 0) } 168 169 return 0 170}