ajhahn.de
← FlashOS
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()
}