ajhahn.de
← FlashOS
Flash 37 lines
// _start argc/argv shim — the default ELF entry point for flibc-linked
// programs that take command-line arguments. The kernel's sys_execve
// path (src/execve.zig) lays an argv block on the new user stack and
// erets to e_entry with `x0 = argc`, `x1 = argv` per AAPCS64; this
// trampolines into the program's `main(argc, argv)`.
//
// Why a typed shim rather than a `callconv(.naked)` register read:
// declaring argc/argv as parameters makes the compiler treat x0/x1 as
// live-in arguments. The standard prologue (save fp/lr, adjust sp) never
// touches x0/x1 before they are forwarded to `main`, so there is no
// prologue to dodge. `main` is declared `noreturn` — flibc programs exit
// via `flibc.exit`, never by returning — so the shim needs no fallback
// exit path and pulls in no syscall dependency.
//
// Why a standalone module instead of an entry in flibc/process.zig:
// `flibc.zig` re-exports `process`, so anything defined there is compiled
// into every flibc-linked program — including demos that define their own
// `_start` (today `flibc_demo.elf`). Zig 0.16 rejects two `_start`
// exports in one compilation as an "exported symbol collision" regardless
// of linkage (weak does not defer to the linker here), so the shim cannot
// live in flibc's always-imported graph. Programs that want it opt in by
// importing this module as `flibc_start` and forcing its emission with
// `comptime { _ = @import("flibc_start"); }`; programs with a bespoke
// entry simply do not import it and keep their own `_start`. The new
// argv_echo demo and the future fsh coreutils take the shim; the legacy
// hello/stackbomb/flibc_demo payloads keep their own entry.

extern fn main(argc usize, argv argv) callconv(.c) noreturn

fn _start_shim(argc usize, argv argv) callconv(.c) noreturn {
    main(argc, argv)
}

comptime {
    #export(&_start_shim, .{ .name = "_start", .linkage = .strong })
}