Flash 43 lines
// Payload for [TEST] execve — proves the sys_execve path end to end:
// argv arrives on the freshly mapped user stack as x0 = argc, x1 = argv
// (AAPCS64), and the ELF is deliberately > 4 KiB so it can only load
// through sys_execve's PT_LOAD streaming. A single-page demo would also
// fit the legacy sys_exec snapshot cap and prove nothing about the cap
// being gone. The body walks argv[0..argc] and printf("%s\n", …) each,
// then exits.
//
// Entry: the flibc _start argc/argv shim, pulled into the compilation by
// the `link "flibc_start"` below. The shim's `extern fn main` binds to
// the `export fn main` here.
//
// Build: aarch64-freestanding ET_EXEC via build.zig (pie=false, strip,
// ReleaseSmall, hello-style page caps), staged at /test/argv_echo.elf.
use flibc
link "flibc_start"
// .rodata padding that forces the linked ELF past one 4 KiB page so it
// can only travel sys_execve's streaming loader. keep_pad() makes &PAD
// escape via a volatile-asm memory clobber so --gc-sections cannot drop
// the otherwise-unreferenced global.
const PAD [4096]u8 linksection(".rodata") = .{0xAB} ** 4096
inline fn keep_pad() {
asm volatile (""
:
: [p] "r" (&PAD),
: .{ .memory = true })
}
export fn main(argc usize, argv argv) noreturn {
keep_pad()
var i usize = 0
while i < argc {
s := argv[i] orelse break
flibc.printf("%s\n", .{s})
i += 1
}
flibc.exit()
}