crt0.nx source
↩ module page · 56 lines · 2412 B
1// crt0.nx -- sovereign `_start` stub for NishiLang programs.
2//
3// Replaces the libc/crt0 that C programs silently link against.
4// The Linux kernel hands us a stack laid out like:
5//
6// sp ------------------------> argc (i64)
7// sp + 8 --------------------> argv[0] (*u8, program name)
8// sp + 16 -------------------> argv[1] (*u8, first real arg)
9// ...
10// sp + 8 + argc*8 -----------> NULL
11// ... envp[0], ..., NULL, auxv...
12//
13// We translate that into the (argc, argv) signature parse.nx expects
14// for main() -- a0 = argc, a1 = argv -- then call main. On return,
15// exit(a0) via the SYS_exit (93) syscall.
16//
17// This file defines `_start` only as source TEXT because NishiLang
18// doesn't yet have a way to declare symbols at specific file offsets
19// or pin a function's emission order. Drivers emit this text at the
20// head of their assembled output so it lands at the ELF entry point.
21//
22// Usage (see runtime/nxc.nx):
23// emit_start_stub(asm_buf)
24// // ... emit user's functions ...
25// assemble(asm_buf, ...)
26// write_elf(code, code_len, fd)
27
28// Canonical nx_-prefixed import per family convention. Was
29// "outbuf.nx" which transitively pulled in syscalls.nx (no nx_
30// prefix) -- duplicated sys_* symbols against nx_syscalls.nx
31// that the rest of the self-host chain uses. Result: link-time
32// "symbol sys_ioctl already defined" failure on every nxc.elf
33// rebuild (2026-05-16 bug class). Fixed by importing nx_outbuf.nx
34// (the canonical nx_-prefixed variant) which imports nx_syscalls.nx.
35import "nx_outbuf.nx"
36
37// Inject the 5-instruction `_start` prologue + epilogue into `o`.
38// Expected layout after assembly:
39// 0x10078: ld a0, 0(sp) ; 0x00013503
40// 0x1007C: addi a1, sp, 8 ; 0x00810593
41// 0x10080: call main ; 0x???????ef (patched by nxasm)
42// 0x10088: li a7, 93 ; 0x05D00893
43// 0x1008C: ecall ; 0x00000073
44//
45// Total: 20 bytes (call is JAL = 4 bytes). Entry point stays at 0x10078.
46func emit_crt0_start(o: *OutBuf) -> i64 {
47 out_str(o, "\n .text\n")
48 out_str(o, " .globl _start\n")
49 out_str(o, "_start:\n")
50 out_str(o, " ld a0, 0(sp)\n")
51 out_str(o, " addi a1, sp, 8\n")
52 out_str(o, " call main\n")
53 out_str(o, " li a7, 93\n")
54 out_str(o, " ecall\n")
55 return 0
56}