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}