Pass arguments to main via runtime.process.Init, dispatched on signature
This commit is contained in:
+4
-2
@@ -90,8 +90,10 @@ process) instead of silently corrupting the image
|
|||||||
(`buildEntryStack` in `system/kernel/process.zig`); `argv[0]` is always the path
|
(`buildEntryStack` in `system/kernel/process.zig`); `argv[0]` is always the path
|
||||||
or initial-ramdisk name the process was spawned as, and `system_spawn`'s optional
|
or initial-ramdisk name the process was spawned as, and `system_spawn`'s optional
|
||||||
argument blob becomes `argv[1..]`. The runtime's `_start`
|
argument blob becomes `argv[1..]`. The runtime's `_start`
|
||||||
(`library/runtime/start.zig`) hands the block to `rt_start`, which exposes it as
|
(`library/runtime/start.zig`) hands the block to `rt_start`, which builds a
|
||||||
`runtime.argumentCount()` / `runtime.argument(i)`. A C runtime's `crt0` would walk
|
`runtime.process.Init` from it and passes that to the program's `main`
|
||||||
|
(`pub fn main(init: runtime.process.Init)`; a parameterless `main()` is also
|
||||||
|
accepted). A C runtime's `crt0` would walk
|
||||||
the identical layout unmodified — that's the compatibility being bought. The
|
the identical layout unmodified — that's the compatibility being bought. The
|
||||||
`args` test proves the round trip.
|
`args` test proves the round trip.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
//! Process-level runtime types: what a user program receives at entry. Mirrors
|
||||||
|
//! the spirit of `std.process.Init.Minimal` in danos terms — std's `Args` holds
|
||||||
|
//! no data on freestanding targets, so the type is danos's own.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// Everything a program receives at entry. Passed to
|
||||||
|
/// `pub fn main(init: runtime.process.Init)`; programs that need nothing keep
|
||||||
|
/// `pub fn main() void`. An `environment` field is added here once the kernel
|
||||||
|
/// passes a non-empty envp (today it is always empty — see docs/sysv.md).
|
||||||
|
pub const Init = struct {
|
||||||
|
arguments: Arguments,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The process arguments (argc/argv), parsed from the kernel-built System V
|
||||||
|
/// entry block. The bytes live in the entry block at the top of the stack page,
|
||||||
|
/// NUL-terminated, valid for the process's lifetime.
|
||||||
|
pub const Arguments = struct {
|
||||||
|
/// argc — at least 1: argument 0 is the path or name this binary was
|
||||||
|
/// spawned as.
|
||||||
|
count: usize,
|
||||||
|
/// The argv pointers in the entry block (NULL-terminated after `count`
|
||||||
|
/// entries).
|
||||||
|
vector: [*]const [*:0]const u8,
|
||||||
|
|
||||||
|
/// Argument `index` (0 = the program's own path/name), or null if out of
|
||||||
|
/// range.
|
||||||
|
pub fn get(arguments: Arguments, index: usize) ?[:0]const u8 {
|
||||||
|
if (index >= arguments.count) return null;
|
||||||
|
return std.mem.span(arguments.vector[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate(arguments: Arguments) Iterator {
|
||||||
|
return .{ .arguments = arguments };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Iterator = struct {
|
||||||
|
arguments: Arguments,
|
||||||
|
index: usize = 0,
|
||||||
|
|
||||||
|
pub fn next(iterator: *Iterator) ?[:0]const u8 {
|
||||||
|
const argument = iterator.arguments.get(iterator.index) orelse return null;
|
||||||
|
iterator.index += 1;
|
||||||
|
return argument;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
//! const runtime = @import("runtime");
|
//! const runtime = @import("runtime");
|
||||||
//! pub const panic = runtime.panic;
|
//! pub const panic = runtime.panic;
|
||||||
//! comptime { _ = &runtime.start._start; } // pull the entry shim in
|
//! comptime { _ = &runtime.start._start; } // pull the entry shim in
|
||||||
//! and a `pub fn main() void`.
|
//! and a `pub fn main() void` or `pub fn main(init: runtime.process.Init) void`
|
||||||
|
//! (arguments arrive via `init`).
|
||||||
|
|
||||||
pub const system = @import("system.zig");
|
pub const system = @import("system.zig");
|
||||||
pub const heap = @import("heap.zig");
|
pub const heap = @import("heap.zig");
|
||||||
@@ -26,10 +27,8 @@ pub const dma = @import("dma.zig");
|
|||||||
/// Re-exported so a user binary can `pub const panic = runtime.panic;`.
|
/// Re-exported so a user binary can `pub const panic = runtime.panic;`.
|
||||||
pub const panic = start.panic;
|
pub const panic = start.panic;
|
||||||
|
|
||||||
/// Process arguments (argc/argv, parsed from the kernel-built entry stack):
|
/// Process entry types: the `Init` handed to `main`, and its `Arguments`.
|
||||||
/// `argument(0)` is the path or name this binary was spawned as.
|
pub const process = @import("process.zig");
|
||||||
pub const argumentCount = start.argumentCount;
|
|
||||||
pub const argument = start.argument;
|
|
||||||
|
|
||||||
/// The heap as a `std.mem.Allocator`, for Zig `std` containers in user code.
|
/// The heap as a `std.mem.Allocator`, for Zig `std` containers in user code.
|
||||||
pub const allocator = heap.allocator;
|
pub const allocator = heap.allocator;
|
||||||
|
|||||||
+51
-24
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const system = @import("system.zig");
|
const system = @import("system.zig");
|
||||||
|
const process = @import("process.zig");
|
||||||
|
|
||||||
/// The kernel enters at `_start` with rsp 16-aligned, pointing at the System V
|
/// The kernel enters at `_start` with rsp 16-aligned, pointing at the System V
|
||||||
/// process-entry block it built: argc, argv pointers, NULL, envp terminator, the
|
/// process-entry block it built: argc, argv pointers, NULL, envp terminator, the
|
||||||
@@ -20,35 +21,61 @@ pub export fn _start() callconv(.naked) noreturn {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The process-entry block, recorded by `rt_start` for the accessors below.
|
|
||||||
var argument_count: usize = 0;
|
|
||||||
var argument_vector: [*]const u64 = undefined;
|
|
||||||
|
|
||||||
/// The first Zig frame, entered with `stack` pointing at the kernel-built entry
|
/// The first Zig frame, entered with `stack` pointing at the kernel-built entry
|
||||||
/// block. Record argc/argv for the accessors, then hand control to the program's
|
/// block. Build the `process.Init` from it and dispatch to the program's `main`,
|
||||||
/// `main`. The heap is lazy (first alloc grows it), so there is no other runtime
|
/// whose signature is inspected at comptime. The heap is lazy (first alloc grows
|
||||||
/// init to order here.
|
/// it), so there is no other runtime init to order here.
|
||||||
export fn rt_start(stack: [*]const u64) callconv(.c) noreturn {
|
export fn rt_start(stack: [*]const u64) callconv(.c) noreturn {
|
||||||
argument_count = stack[0];
|
const init: process.Init = .{ .arguments = .{
|
||||||
argument_vector = stack + 1;
|
.count = stack[0],
|
||||||
|
.vector = @ptrCast(stack + 1),
|
||||||
|
} };
|
||||||
|
system.exit(callMain(init));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Comptime-dispatch on root.main's signature, in the spirit of std's start.zig:
|
||||||
|
/// zero parameters or one `process.Init`; returns void, noreturn, u8, !void, or !u8.
|
||||||
|
fn callMain(init: process.Init) u8 {
|
||||||
const root = @import("root"); // the user binary's root source file
|
const root = @import("root"); // the user binary's root source file
|
||||||
root.main();
|
const main_information = @typeInfo(@TypeOf(root.main)).@"fn";
|
||||||
system.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of process arguments (argc). At least 1: argument 0 is the path or
|
const call_arguments = switch (main_information.params.len) {
|
||||||
/// name this binary was spawned as.
|
0 => .{},
|
||||||
pub fn argumentCount() usize {
|
1 => arguments: {
|
||||||
return argument_count;
|
const Parameter = main_information.params[0].type orelse
|
||||||
}
|
@compileError("main's parameter must be runtime.process.Init (not anytype)");
|
||||||
|
if (Parameter != process.Init)
|
||||||
|
@compileError("main's parameter must be runtime.process.Init, found " ++ @typeName(Parameter));
|
||||||
|
break :arguments .{init};
|
||||||
|
},
|
||||||
|
else => @compileError("main takes no parameters or a single runtime.process.Init"),
|
||||||
|
};
|
||||||
|
|
||||||
/// Process argument `index` (0 = the program's own path/name), or an empty slice
|
const ReturnType = main_information.return_type.?;
|
||||||
/// if out of range. The bytes live in the entry block at the top of the stack
|
switch (@typeInfo(ReturnType)) {
|
||||||
/// page, NUL-terminated, valid for the process's lifetime.
|
.noreturn => @call(.auto, root.main, call_arguments),
|
||||||
pub fn argument(index: usize) []const u8 {
|
.void => {
|
||||||
if (index >= argument_count) return "";
|
@call(.auto, root.main, call_arguments);
|
||||||
const string: [*:0]const u8 = @ptrFromInt(argument_vector[index]);
|
return 0;
|
||||||
return std.mem.span(string);
|
},
|
||||||
|
.int => {
|
||||||
|
if (ReturnType != u8)
|
||||||
|
@compileError("main's integer return type must be u8, found " ++ @typeName(ReturnType));
|
||||||
|
return @call(.auto, root.main, call_arguments);
|
||||||
|
},
|
||||||
|
.error_union => {
|
||||||
|
const payload = @call(.auto, root.main, call_arguments) catch |err| {
|
||||||
|
var buffer: [128]u8 = undefined;
|
||||||
|
const line = std.fmt.bufPrint(&buffer, "main returned error: {s}\n", .{@errorName(err)}) catch "main returned an error\n";
|
||||||
|
_ = system.write(line);
|
||||||
|
return 1; // distinct from panic's 127
|
||||||
|
};
|
||||||
|
if (@TypeOf(payload) == void) return 0;
|
||||||
|
if (@TypeOf(payload) == u8) return payload;
|
||||||
|
@compileError("main's error-union payload must be void or u8, found " ++ @typeName(@TypeOf(payload)));
|
||||||
|
},
|
||||||
|
else => @compileError("main must return void, noreturn, u8, !void, or !u8, found " ++ @typeName(ReturnType)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// No runtime to unwind into — report a panic as a nonzero exit code.
|
/// No runtime to unwind into — report a panic as a nonzero exit code.
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ fn burnStack(depth: usize) u8 {
|
|||||||
return touch[0] +% burnStack(depth - 1);
|
return touch[0] +% burnStack(depth - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main(init: runtime.process.Init) void {
|
||||||
if (runtime.argumentCount() <= 1) {
|
if (init.arguments.count <= 1) {
|
||||||
// First instance: spawn the second with real arguments, then exit.
|
// First instance: spawn the second with real arguments, then exit.
|
||||||
_ = runtime.system.spawnWithArguments("args-echo", &.{ "alpha", "beta-42" });
|
_ = runtime.system.spawnWithArguments("args-echo", &.{ "alpha", "beta-42" });
|
||||||
return;
|
return;
|
||||||
@@ -36,8 +36,8 @@ pub fn main() void {
|
|||||||
const prefix = "args:";
|
const prefix = "args:";
|
||||||
@memcpy(buffer[0..prefix.len], prefix);
|
@memcpy(buffer[0..prefix.len], prefix);
|
||||||
var len: usize = prefix.len;
|
var len: usize = prefix.len;
|
||||||
for (0..runtime.argumentCount()) |i| {
|
var iterator = init.arguments.iterate();
|
||||||
const argument = runtime.argument(i);
|
while (iterator.next()) |argument| {
|
||||||
if (len + 1 + argument.len + 1 > buffer.len) break;
|
if (len + 1 + argument.len + 1 > buffer.len) break;
|
||||||
buffer[len] = ' ';
|
buffer[len] = ' ';
|
||||||
len += 1;
|
len += 1;
|
||||||
|
|||||||
@@ -48,10 +48,8 @@ fn awaitChildExit(endpoint: runtime.ipc.Handle) u32 {
|
|||||||
return received.childProcessId();
|
return received.childProcessId();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main(init: runtime.process.Init) void {
|
||||||
if (runtime.argumentCount() <= 1) return; // spawned bare (ramdisk sweep): stay silent
|
const role = init.arguments.get(1) orelse return; // spawned bare (ramdisk sweep): stay silent
|
||||||
|
|
||||||
const role = runtime.argument(1);
|
|
||||||
if (std.mem.eql(u8, role, "sleeper")) {
|
if (std.mem.eql(u8, role, "sleeper")) {
|
||||||
while (true) runtime.system.sleep(500);
|
while (true) runtime.system.sleep(500);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user