code wiki / (root) / nx_readelf.nx

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}