Files
ww/rt/start.s
Hojun-Cho 4ab530c24d rt+lib/os+test: capture envp; add os.getenv
Capture envp from the kernel-supplied stack into a DATAW slot during
_start's prologue (before CALL main), and expose it via a `rt_envp`
TEXT getter. lib/os.getenv binds the getter as `@symbol("rt_envp")
fn rtenvp() **u8` — the getter-fn pattern works around @symbol-on-let
not being supported by the compiler yet (silent miscompile otherwise).

`os.getenv(name: str) (str | void)` matches Hare's os::getenv surface:
walks the NUL-terminated envp table, "name=" prefix-matches with an
explicit `=` boundary check so prefixes don't false-match longer
names, returns the value as a borrowed str view. Empty value (env
"FOO=") returns len=0 str, not void — void is reserved for "name
not present at all".

Cohort coverage in lib/os/ostest.ww + test/wcc/974_getenv_run.c:
set / empty / unset / prefix-no-match (4 @test fns).
2026-05-15 17:13:32 +09:00

49 lines
2.1 KiB
ArmAsm

// rt/start.s process entry. Linux sets up the stack so that on
// entry [SP] holds argc, [SP+8] starts argv (NULL-terminated array
// of *u8), [SP+8+(argc+1)*8] starts envp (also NULL-terminated).
//
// We pull argc into DI and the argv pointer into SI, then jump to
// `main`. ww programs that declare `fn main(argc: i32, argv: **u8)
// i32` see them; programs declaring `fn main() i32` simply ignore
// the regs. Either way, main's return value is fed to the exit
// syscall.
//
// envp is captured into the rt_envp_slot DATAW cell before CALL main
// so lib/os.getenv (via the rt_envp getter below) can walk it. We
// compute envp = SP + 16 + argc*8 in AX using three doublings and
// adds w6a's syntax doesn't ship SIB-style indexed addressing
// (no `LEAQ 16(SP)(DI*8), AX`), so the explicit shift-by-three is
// the portable form. AX/DI/SI are scratch on entry; no callee-
// saved discipline applies until we reach main.
TEXT _start,$0
MOVQ (SP), DI // argc DI (1st arg to main)
LEAQ 8(SP), SI // &argv[0] SI (2nd arg to main)
MOVQ DI, AX // AX = argc
ADDQ AX, AX // AX = argc * 2
ADDQ AX, AX // AX = argc * 4
ADDQ AX, AX // AX = argc * 8
ADDQ SP, AX // AX = SP + argc*8
ADDQ $16, AX // AX = SP + argc*8 + 16 = &argv[argc+1] = envp
MOVQ AX, rt_envp_slot(SB)
CALL main(SB)
MOVQ AX, DI
MOVQ $60, AX
SYSCALL
// rt_envp getter that returns the envp pointer captured at process
// entry. Bound from lib/os via `@symbol("rt_envp") fn rtenvp() **u8;`,
// matching the rt_syscall / rt_alloc / rt_abort pattern. Read-only
// view of the kernel-supplied table; the bytes live for the process
// lifetime. A future setenv that grows the table re-points the slot
// (separate task).
TEXT rt_envp,$0
MOVQ rt_envp_slot(SB), AX
RET
// rt_envp_slot 8-byte writable cell holding the envp pointer.
// Initialised to zero in .data; _start overwrites it before
// transferring control to main. DATAW gives us the writable .data
// slot (w6a has no GLOBL; same shape as lib/log's silent/default/
// global cells in lib/log/log.s).
DATAW rt_envp_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00"