nx_readelf.nx source
↩ module page · 178 lines · 6253 B
1// nx_readelf.nx -- sovereign readelf (one-stop ELF dump).
2//
3// Combines our existing objdump + nm + dwarf-section enumeration
4// into a single output that mirrors `readelf -a`. Useful as the
5// canonical "show me everything in this ELF" tool for sovereign
6// debugging without binutils.
7//
8// Sections:
9// * ELF header
10// * Program headers (with type / flags / vaddr / filesz / memsz)
11// * Section headers (idx / name / type / addr / offset / size)
12// * Symbol table (one entry per symbol)
13// * (DWARF section sizes, if present)
14//
15// Pairs with nx_objdump (subset) + nx_nm.
16
17// nx_safety_envelope:
18// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
19// sil_target: SIL1
20// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
21// verdict: NOT_YET_EVALUATED
22
23import "syscalls.nx"
24import "nx_elf_read.nx"
25import "nx_strfmt.nx"
26import "nx_objdump.nx"
27import "nx_nm.nx"
28
29// Read program header at index `idx`. We re-implement here rather
30// than importing nx_loader because nx_loader conditionally
31// allocates sim memory; for inspection we just want the raw fields.
32//
33// Elf64_Phdr layout: 56 bytes, little-endian.
34struct NxReElfPhdr {
35 p_type: i64,
36 p_flags: i64,
37 p_offset: i64,
38 p_vaddr: i64,
39 p_paddr: i64,
40 p_filesz: i64,
41 p_memsz: i64,
42 p_align: i64,
43}
44
45const NX_RE_PHDR_BYTES: i64 = 64
46
47func nx_readelf_get_phdr(buf: *u8, h: *NxElfHeader, idx: i64) -> *NxReElfPhdr {
48 let raw: *u8 = sys_mmap(NX_RE_PHDR_BYTES)
49 let p: *NxReElfPhdr = raw as *NxReElfPhdr
50 let off: i64 = h.e_phoff + idx * h.e_phentsize
51 p.p_type = nx_elf_get_u32_le(buf, off + 0)
52 p.p_flags = nx_elf_get_u32_le(buf, off + 4)
53 p.p_offset = nx_elf_get_u64_le(buf, off + 8)
54 p.p_vaddr = nx_elf_get_u64_le(buf, off + 16)
55 p.p_paddr = nx_elf_get_u64_le(buf, off + 24)
56 p.p_filesz = nx_elf_get_u64_le(buf, off + 32)
57 p.p_memsz = nx_elf_get_u64_le(buf, off + 40)
58 p.p_align = nx_elf_get_u64_le(buf, off + 48)
59 return p
60}
61
62// Print program headers section.
63func nx_readelf_program_headers(buf: *u8, len: i64, fd: i64) -> i64 {
64 let h: *NxElfHeader = nx_elf_parse_header(buf, len)
65 if h.valid != 1 { return -1 }
66 if h.e_phnum == 0 {
67 sys_write(fd, "Program headers:\n (none)\n" as *u8, 26)
68 return 0
69 }
70 sys_write(fd, "Program headers:\n" as *u8, 17)
71 sys_write(fd, " idx type flags vaddr offset filesz memsz\n" as *u8, 67)
72
73 let argv_raw: *u8 = sys_mmap(64)
74 let argv: *i64 = argv_raw as *i64
75
76 var i: i64 = 0
77 while i < h.e_phnum {
78 let p: *NxReElfPhdr = nx_readelf_get_phdr(buf, h, i)
79 argv[0] = i
80 nx_fmt_to_fd(fd, " %d " as *u8, argv, 1)
81
82 // Type.
83 if p.p_type == 0 { sys_write(fd, "NULL " as *u8, 8) }
84 if p.p_type == 1 { sys_write(fd, "LOAD " as *u8, 8) }
85 if p.p_type == 2 { sys_write(fd, "DYNAMIC " as *u8, 8) }
86 if p.p_type == 3 { sys_write(fd, "INTERP " as *u8, 8) }
87 if p.p_type == 4 { sys_write(fd, "NOTE " as *u8, 8) }
88 if p.p_type == 6 { sys_write(fd, "PHDR " as *u8, 8) }
89 if p.p_type == 7 { sys_write(fd, "TLS " as *u8, 8) }
90
91 // Flags as RWX bitset.
92 let r_bit: i64 = (p.p_flags >> 2) & 0x1
93 let w_bit: i64 = (p.p_flags >> 1) & 0x1
94 let x_bit: i64 = p.p_flags & 0x1
95 if r_bit == 1 { sys_write(fd, "R" as *u8, 1) } else { sys_write(fd, "-" as *u8, 1) }
96 if w_bit == 1 { sys_write(fd, "W" as *u8, 1) } else { sys_write(fd, "-" as *u8, 1) }
97 if x_bit == 1 { sys_write(fd, "X" as *u8, 1) } else { sys_write(fd, "-" as *u8, 1) }
98 sys_write(fd, " " as *u8, 2)
99
100 argv[0] = p.p_vaddr
101 argv[1] = p.p_offset
102 argv[2] = p.p_filesz
103 argv[3] = p.p_memsz
104 nx_fmt_to_fd(fd, "0x%X 0x%X 0x%X 0x%X\n" as *u8, argv, 4)
105
106 i = i + 1
107 }
108 return 0
109}
110
111// One-shot "everything" dumper.
112func nx_readelf_all(buf: *u8, len: i64, fd: i64) -> i64 {
113 if nx_objdump_elf_header(buf, len, fd) < 0 { return -1 }
114 sys_write(fd, "\n" as *u8, 1)
115 nx_readelf_program_headers(buf, len, fd)
116 sys_write(fd, "\n" as *u8, 1)
117 nx_objdump_section_headers(buf, len, fd)
118 sys_write(fd, "\n" as *u8, 1)
119
120 // Symbols if present.
121 let h: *NxElfHeader = nx_elf_parse_header(buf, len)
122 if h.valid == 1 {
123 let sym_idx: i64 = nx_nm_find_symtab(buf, h)
124 if sym_idx >= 0 {
125 sys_write(fd, "Symbol table:\n" as *u8, 14)
126 nx_nm_print(buf, len, fd)
127 }
128 }
129
130 return 0
131}
132
133// ---- self-test ---------------------------------------------------
134
135func main() -> i64 {
136 // Sanity-check the program-header decoder on a hand-built ELF
137 // that has 1 PT_LOAD segment. We don't need to RUN it, just
138 // verify decoded fields match what was encoded.
139 let elf: *u8 = sys_mmap(256)
140 var i: i64 = 0
141 while i < 256 { elf[i] = 0; i = i + 1 }
142
143 elf[0] = 0x7F; elf[1] = 0x45; elf[2] = 0x4C; elf[3] = 0x46
144 elf[4] = 2; elf[5] = 1; elf[6] = 1
145 elf[16] = 2; elf[18] = 0xF3
146 elf[32] = 64 // phoff = 64
147 elf[52] = 64 // ehsize
148 elf[54] = 56 // phentsize
149 elf[56] = 1 // phnum
150
151 // Program header at offset 64:
152 // p_type = 1 (LOAD)
153 // p_flags = 5 (R|X)
154 // p_offset = 120
155 // p_vaddr = 0x10000
156 // p_filesz = 12
157 // p_memsz = 12
158 elf[64] = 1
159 elf[68] = 5
160 elf[72] = 120
161 elf[80] = 0x00; elf[81] = 0x00; elf[82] = 0x01; elf[83] = 0x00
162 elf[96] = 12
163 elf[104] = 12
164
165 let h: *NxElfHeader = nx_elf_parse_header(elf, 256)
166 if h.valid != 1 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
167 if h.e_phnum != 1 { return __syscall(93, 11, 0, 0, 0, 0, 0) }
168
169 let p: *NxReElfPhdr = nx_readelf_get_phdr(elf, h, 0)
170 if p.p_type != 1 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
171 if p.p_flags != 5 { return __syscall(93, 21, 0, 0, 0, 0, 0) }
172 if p.p_offset != 120 { return __syscall(93, 22, 0, 0, 0, 0, 0) }
173 if p.p_vaddr != 0x10000 { return __syscall(93, 23, 0, 0, 0, 0, 0) }
174 if p.p_filesz != 12 { return __syscall(93, 24, 0, 0, 0, 0, 0) }
175 if p.p_memsz != 12 { return __syscall(93, 25, 0, 0, 0, 0, 0) }
176
177 return 0
178}