nx_loader.nx
buildroot/runtime/nx_loader.nx
about
nx_loader.nx -- ELF segment loader for nx_rv64_sim.
Closes the loop: nx_elf_read parses, nx_loader copies PT_LOAD
segments into a memory buffer, nx_rv64_sim executes from there.
With these three modules we can run an RV64 ELF entirely in
NishiLang -- no qemu, no docker, no Linux process boundary.
This is THE off-C bootstrap-verification capability.
What we do:
1. Parse the ELF header.
2. For each program header:
* If type = PT_LOAD, copy `p_filesz` bytes from
ELF[p_offset..p_offset+p_filesz) to mem[p_vaddr...]
* Then zero `p_memsz - p_filesz` bytes (BSS region).
3. Return the entry point so the simulator can set PC.
Memory model: we treat `mem_base[0..mem_size)` as a flat region
where addresses are file-vaddrs. If p_vaddr is 0x10000, then
mem_base[0x10000] is where the segment lands. The caller
allocates a mem buffer big enough to hold all loaded segments;
today that means `>= max_vaddr + max_segment_size`.
PT_LOAD is the only program-header type we handle; PT_PHDR /
PT_INTERP / etc. are silently skipped (irrelevant for static
RV64 ELFs from elf_writer).
dependencies 2 imports · 5 importers
imports: syscalls.nxnx_elf_read.nx
imported by: nx_nxc_native_wrap.nxnx_smoke_sim.nxnx_sovereign_runner.nxnx_toolchain.nxnxc_native_wrap.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 40 | struct NxElfPhdr |
consts
| 51 | const NX_ELF_PHDR_BYTES: i64 = 64 |
| 53 | const NX_PT_NULL: i64 = 0 |
| 54 | const NX_PT_LOAD: i64 = 1 |
| 55 | const NX_PT_DYNAMIC: i64 = 2 |
| 56 | const NX_PT_INTERP: i64 = 3 |
| 57 | const NX_PT_PHDR: i64 = 6 |
functions
| 61 | func nx_loader_read_phdr(buf: *u8, off: i64) -> *NxElfPhdr |
| 77 | func nx_loader_memcpy(dst: *u8, src: *u8, n: i64) -> i64 called by 1: nx_loader_load |
| 86 | func nx_loader_memzero(dst: *u8, n: i64) -> i64 called by 1: nx_loader_load |
| 101 | func nx_loader_load(elf_buf: *u8, elf_len: i64, |
| 138 | func main() -> i64 calls 1: nx_loader_load |