code wiki / (root) / nx_elf_inspect.nx

nx_elf_inspect.nx source

↩ module page · 109 lines · 5389 B

1// nx_elf_inspect.nx -- SOVEREIGN ELF inspector: answers "is this binary truly sovereign?" = statically linked 2// with ZERO dynamic dependencies, WITHOUT the shell tools `file` + `readelf`. Parses the ELF64 program-header 3// table for a PT_INTERP (an interpreter => dynamically linked) and a PT_DYNAMIC segment, then walks the dynamic 4// array for DT_NEEDED (shared-library) entries. This is the crux capability for the bench/gate_nx_sovereign_deps.sh 5// gate (which uses `file | grep "statically linked"` + `readelf -d | grep NEEDED`) and, broadly, the binary-level 6// sovereignty check the ecosystem lacked (source-scan proves no lib REFERENCES; this proves the shipped ELF has 7// no lib DEPENDENCIES). Sovereign (syscalls only). license_tier: ORIGINAL expect_exit: 0 (on a static ELF) 8// 9// Usage: nx_elf_inspect <file> -- exit 0 = STATIC/sovereign (no interpreter, 0 NEEDED); exit 1 = DYNAMIC. 10import "nx_syscalls.nx" 11 12const EI_CAP: i64 = 16777216 // 16 MiB read cap (organ + system binaries are well under this) 13 14func ei_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 15func ei_putn(v: i64) -> i64 { 16 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 17 var m: i64 = v 18 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 19 let d: *u8 = sys_mmap(24); var k: i64 = 0 20 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 21 let o: *u8 = sys_mmap(24); var i: i64 = 0 22 while i < k { o[i] = d[k - 1 - i]; i = i + 1 } 23 sys_write(1, o, k); return 0 24} 25 26func ei_read_file(path: *u8, buf: *u8, cap: i64) -> i64 { 27 let fd: i64 = sys_openat_rd(path) 28 if fd < 0 { return 0 } 29 var total: i64 = 0 30 var go: i64 = 1 31 while go == 1 { 32 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) 33 if r <= 0 { go = 0 } else { total = total + r; if total >= cap { go = 0 } } 34 } 35 sys_close(fd) 36 return total 37} 38 39// little-endian reads at byte offset o (offsets/values here are small, no sign concern) 40func ei_u16(buf: *u8, o: i64) -> i64 { return (buf[o] as i64) | ((buf[o + 1] as i64) << 8) } 41func ei_u32(buf: *u8, o: i64) -> i64 { 42 return (buf[o] as i64) | ((buf[o + 1] as i64) << 8) | ((buf[o + 2] as i64) << 16) | ((buf[o + 3] as i64) << 24) 43} 44func ei_u64(buf: *u8, o: i64) -> i64 { 45 var v: i64 = 0; var i: i64 = 0 46 while i < 8 { v = v | ((buf[o + i] as i64) << (8 * i)); i = i + 1 } 47 return v 48} 49 50func main(argc: i64, argv: *i64) -> i64 { 51 if argc < 2 { ei_puts("usage: nx_elf_inspect <file> (exit 0 = STATIC/sovereign, 1 = DYNAMIC)\n" as *u8); sys_exit(2); return 2 } 52 let path: *u8 = argv[1] as *u8 53 let buf: *u8 = sys_mmap(EI_CAP) 54 let n: i64 = ei_read_file(path, buf, EI_CAP) 55 if n < 64 { ei_puts("nx_elf_inspect: not an ELF (file < 64 bytes or unreadable): " as *u8); ei_puts(path); ei_puts("\n" as *u8); sys_exit(1); return 1 } 56 // magic 0x7f 'E' 'L' 'F' + class byte (2 = ELF64) 57 if (buf[0] & 0xff) != 127 { ei_puts("nx_elf_inspect: not an ELF (bad magic)\n" as *u8); sys_exit(1); return 1 } 58 if buf[1] != (69 as u8) { ei_puts("nx_elf_inspect: not an ELF (bad magic)\n" as *u8); sys_exit(1); return 1 } // 'E' 59 if buf[2] != (76 as u8) { ei_puts("nx_elf_inspect: not an ELF (bad magic)\n" as *u8); sys_exit(1); return 1 } // 'L' 60 if buf[3] != (70 as u8) { ei_puts("nx_elf_inspect: not an ELF (bad magic)\n" as *u8); sys_exit(1); return 1 } // 'F' 61 if buf[4] != (2 as u8) { ei_puts("nx_elf_inspect: not ELF64 (32-bit unsupported)\n" as *u8); sys_exit(1); return 1 } 62 63 let phoff: i64 = ei_u64(buf, 32) // e_phoff 64 let phentsize: i64 = ei_u16(buf, 54) // e_phentsize 65 let phnum: i64 = ei_u16(buf, 56) // e_phnum 66 67 var has_interp: i64 = 0 68 var has_dyn: i64 = 0 69 var dyn_off: i64 = 0 70 var i: i64 = 0 71 while i < phnum { 72 let ph: i64 = phoff + i * phentsize 73 if ph + 56 <= n { 74 let ptype: i64 = ei_u32(buf, ph) // p_type @+0 75 if ptype == 3 { has_interp = 1 } // PT_INTERP 76 if ptype == 2 { has_dyn = 1; dyn_off = ei_u64(buf, ph + 8) } // PT_DYNAMIC, p_offset @+8 77 } 78 i = i + 1 79 } 80 81 // walk the dynamic array (16-byte entries: d_tag @+0, d_val @+8) counting DT_NEEDED (tag 1) until DT_NULL (0) 82 var needed: i64 = 0 83 if has_dyn == 1 { 84 var d: i64 = dyn_off 85 var go: i64 = 1 86 while go == 1 { 87 if d + 16 > n { go = 0 } else { 88 let tag: i64 = ei_u64(buf, d) 89 if tag == 0 { go = 0 } else { 90 if tag == 1 { needed = needed + 1 } 91 d = d + 16 92 } 93 } 94 } 95 } 96 97 var static_ok: i64 = 1 98 if has_interp == 1 { static_ok = 0 } 99 if needed > 0 { static_ok = 0 } 100 101 ei_puts("=== nx_elf_inspect " as *u8); ei_puts(path); ei_puts(" ===\n interpreter=" as *u8); ei_putn(has_interp) 102 ei_puts(" PT_DYNAMIC=" as *u8); ei_putn(has_dyn); ei_puts(" DT_NEEDED(shared libs)=" as *u8); ei_putn(needed); ei_puts("\n" as *u8) 103 if static_ok == 1 { 104 ei_puts("NX-ELF-INSPECT STATIC: sovereign binary -- no interpreter, 0 dynamic dependencies (raw-syscall bits-up)\n" as *u8) 105 sys_exit(0); return 0 106 } 107 ei_puts("NX-ELF-INSPECT DYNAMIC: NOT sovereign -- has an interpreter or shared-library dependencies\n" as *u8) 108 sys_exit(1); return 1 109}