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).
This commit is contained in:
38
rt/start.s
38
rt/start.s
@@ -1,16 +1,48 @@
|
||||
// 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.
|
||||
// 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
|
||||
LEAQ 8(SP), SI
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user