nx_nxc_native_wrap.nx source
↩ module page · 180 lines · 5392 B
1// nxc_native_wrap.nx -- sovereign bootstrap-verification driver.
2//
3// One-shot main() that proves the off-C runtime path works:
4//
5// 1. Read an RV64 ELF from disk (built via stage 1's gcc-anchor
6// OR via the in-NishiLang nxc.elf compiler).
7// 2. Validate the ELF via nx_elf_read.
8// 3. Allocate a 16 MB flat memory region.
9// 4. Load segments into memory via nx_loader.
10// 5. Set sp to the top of memory minus 64 bytes.
11// 6. Run via nx_rv64_sim with a 100M-instruction budget.
12// 7. Report exit code + instruction count to stderr.
13//
14// Usage:
15// nxc_native_wrap path/to/program.elf
16//
17// Returns:
18// 0 = program executed cleanly via simulator
19// 1 = invalid ELF
20// 2 = load failed (e.g. segment OOB)
21// 3 = simulator hit illegal instruction
22// 4 = budget expired
23// N = the simulated program exited with code N (passed through)
24//
25// Pairs with bootstrap_native.sh. Once this lands + works, we have
26// the full off-C verification: produce ELF in NishiLang, run ELF in
27// NishiLang, compare exit code with stage 1. Sovereign stage 2 is
28// achieved.
29
30// nx_safety_envelope:
31// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
32// sil_target: SIL1
33// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
34// verdict: NOT_YET_EVALUATED
35
36import "nx_syscalls.nx"
37import "nx_elf_read.nx"
38import "nx_loader.nx"
39import "nx_rv64_sim.nx"
40const NX_MAGIC_4194304: i64 = 4194304
41
42const NX_WRAP_MEM_SIZE: i64 = 16777216 // 16 MB sim memory
43const NX_WRAP_BUDGET: i64 = 100000000 // 100M instructions
44
45// Read the entire file at `path` into a fresh mmap'd buffer.
46// Returns null + zero len on error.
47func nx_wrap_read_file(path: *u8, out_len: *i64) -> *u8 {
48 let fd: i64 = sys_openat_rd(path)
49 if fd < 0 {
50 *out_len = 0
51 return 0 as *u8
52 }
53 let cap: i64 = NX_MAGIC_4194304 // 4 MB cap (covers nxc.elf + room)
54 let buf: *u8 = sys_mmap(cap)
55 var total: i64 = 0
56 var go: i64 = 1
57 while go == 1 {
58 let base: i64 = buf as i64
59 let tail: *u8 = (base + total) as *u8
60 let n: i64 = sys_read(fd, tail, cap - total)
61 if n <= 0 { go = 0 }
62 if n > 0 { total = total + n }
63 if total >= cap { go = 0 }
64 }
65 sys_close(fd)
66 *out_len = total
67 return buf
68}
69
70// Print "wrap: " + a NUL-terminated string + "\n" to stderr.
71func nx_wrap_log(s: *u8) -> i64 {
72 sys_write(2, "wrap: " as *u8, 6)
73 var n: i64 = 0
74 while s[n] != 0 { n = n + 1 }
75 sys_write(2, s, n)
76 sys_write(2, "\n" as *u8, 1)
77 return 0
78}
79
80// Print "wrap: " + label + " = " + decimal value + "\n".
81func nx_wrap_logn(label: *u8, v: i64) -> i64 {
82 sys_write(2, "wrap: " as *u8, 6)
83 var nl: i64 = 0
84 while label[nl] != 0 { nl = nl + 1 }
85 sys_write(2, label, nl)
86 sys_write(2, " = " as *u8, 3)
87
88 let buf: *u8 = sys_mmap(32)
89 var n: i64 = v
90 if n < 0 {
91 sys_write(2, "-" as *u8, 1)
92 n = 0 - n
93 }
94 if n == 0 {
95 buf[0] = 0x30
96 sys_write(2, buf, 1)
97 } else {
98 var k: i64 = 0
99 while n > 0 {
100 buf[k] = 0x30 + (n - (n / 10) * 10)
101 n = n / 10
102 k = k + 1
103 }
104 var j: i64 = 0
105 var r: i64 = k - 1
106 while j < r {
107 let t: i64 = buf[j]
108 buf[j] = buf[r]
109 buf[r] = t
110 j = j + 1
111 r = r - 1
112 }
113 sys_write(2, buf, k)
114 }
115 sys_write(2, "\n" as *u8, 1)
116 return 0
117}
118
119func main(argc: i64, argv: *i64) -> i64 {
120 if argc < 2 {
121 nx_wrap_log("usage: nxc_native_wrap <path.elf>" as *u8)
122 return 1
123 }
124 let path: *u8 = (argv[1]) as *u8
125
126 // Stage 1: read the ELF.
127 let elf_len_raw: *u8 = sys_mmap(16)
128 let elf_len: *i64 = elf_len_raw as *i64
129 *elf_len = 0
130 let elf_buf: *u8 = nx_wrap_read_file(path, elf_len)
131 if elf_buf == (0 as *u8) {
132 nx_wrap_log("read failed" as *u8)
133 return 1
134 }
135 nx_wrap_logn("elf bytes" as *u8, *elf_len)
136
137 // Stage 2: validate header.
138 let h: *NxElfHeader = nx_elf_parse_header(elf_buf, *elf_len)
139 if h.valid != 1 {
140 nx_wrap_log("invalid ELF" as *u8)
141 return 1
142 }
143 if h.is_64bit != 1 {
144 nx_wrap_log("not ELF64" as *u8)
145 return 1
146 }
147 if h.e_machine != 0xF3 {
148 nx_wrap_log("not RISC-V" as *u8)
149 return 1
150 }
151 nx_wrap_logn("entry vaddr (hex)" as *u8, h.e_entry)
152 nx_wrap_logn("phnum" as *u8, h.e_phnum)
153
154 // Stage 3: allocate sim memory + load segments.
155 let mem: *u8 = sys_mmap(NX_WRAP_MEM_SIZE)
156 let entry: i64 = nx_loader_load(elf_buf, *elf_len, mem, NX_WRAP_MEM_SIZE)
157 if entry < 0 {
158 nx_wrap_log("load failed" as *u8)
159 return 2
160 }
161 nx_wrap_logn("loaded; entry" as *u8, entry)
162
163 // Stage 4: spin up the CPU + run.
164 let cpu: *NxRv64Cpu = nx_rv64_cpu_new(mem, NX_WRAP_MEM_SIZE, entry)
165 nx_rv64_cpu_set_sp(cpu, NX_WRAP_MEM_SIZE - 64)
166 nx_wrap_log("starting simulator" as *u8)
167 let r: i64 = nx_rv64_run(cpu, NX_WRAP_BUDGET)
168 nx_wrap_logn("instructions executed" as *u8, cpu.insn_count)
169
170 if r == 0 {
171 nx_wrap_logn("program exit code" as *u8, cpu.exit_code)
172 return cpu.exit_code
173 }
174 if r > 0 {
175 nx_wrap_log("BUDGET EXPIRED" as *u8)
176 return 4
177 }
178 nx_wrap_log("ILLEGAL INSTRUCTION" as *u8)
179 return 3
180}