os: graduate SYS_* defs to nr enum
`type nr = enum i64 { READ, WRITE, OPEN, ... }`. syscall0..4 take
`num: nr` so the wrong-arg-order trap is now a compile error
(`syscall1(0i64, ...)` no longer typechecks — it has to be
`syscall1(nr.READ, ...)`).
Internal-only (callers outside os.ww never touched the constants),
so no external API change. ABI is unchanged: nr's storage is i64
and rt_syscall's RDI is unchanged.
The selfhost combined.ww files regenerate as a side effect of
`make wwstage`.
This commit is contained in:
@@ -3,11 +3,11 @@
|
||||
// either in libwwrt.a (rt_syscall trampoline) or libc bindings,
|
||||
// depending on how the program was linked.
|
||||
|
||||
@symbol("rt_syscall") fn syscall0(num: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall0(num: nr) i64;
|
||||
@symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
|
||||
@symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
|
||||
|
||||
@symbol("rt_alloc") fn alloc(n: u64) *void;
|
||||
@symbol("rt_free") fn free(p: *void, n: u64) void;
|
||||
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
|
||||
if (!cond) { abort(msg); };
|
||||
};
|
||||
|
||||
def SYS_READ: i64 = 0;
|
||||
def SYS_WRITE: i64 = 1;
|
||||
def SYS_OPEN: i64 = 2;
|
||||
def SYS_CLOSE: i64 = 3;
|
||||
def SYS_LSEEK: i64 = 8;
|
||||
def SYS_ACCESS: i64 = 21;
|
||||
def SYS_DUP2: i64 = 33;
|
||||
def SYS_GETPID: i64 = 39;
|
||||
def SYS_FORK: i64 = 57;
|
||||
def SYS_EXECVE: i64 = 59;
|
||||
def SYS_EXIT: i64 = 60;
|
||||
def SYS_WAIT4: i64 = 61;
|
||||
def SYS_UNLINK: i64 = 87;
|
||||
def SYS_GETCWD: i64 = 79;
|
||||
def SYS_GETDENTS64: i64 = 217;
|
||||
// Linux amd64 syscall numbers. Internal to this module — passed as
|
||||
// the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
|
||||
// `nr` is the type so the call sites can't accidentally pass an
|
||||
// arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
|
||||
type nr = enum i64 {
|
||||
READ = 0,
|
||||
WRITE = 1,
|
||||
OPEN = 2,
|
||||
CLOSE = 3,
|
||||
LSEEK = 8,
|
||||
ACCESS = 21,
|
||||
DUP2 = 33,
|
||||
GETPID = 39,
|
||||
FORK = 57,
|
||||
EXECVE = 59,
|
||||
EXIT = 60,
|
||||
WAIT4 = 61,
|
||||
UNLINK = 87,
|
||||
GETCWD = 79,
|
||||
GETDENTS64 = 217,
|
||||
};
|
||||
|
||||
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
|
||||
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
|
||||
@@ -54,22 +60,22 @@ export type whence = enum i32 {
|
||||
};
|
||||
|
||||
export fn exit(code: i32) void = {
|
||||
syscall1(SYS_EXIT, code: i64);
|
||||
syscall1(nr.EXIT, code: i64);
|
||||
};
|
||||
|
||||
// Raw, non-fallible primitives. These return Linux's int conventions
|
||||
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
|
||||
// Hare-style fallible API use the wrappers below.
|
||||
export fn write(fd: i32, buf: *u8, n: u64) i64 = {
|
||||
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64);
|
||||
return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
|
||||
};
|
||||
|
||||
export fn read(fd: i32, buf: *u8, n: u64) i64 = {
|
||||
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64);
|
||||
return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
|
||||
};
|
||||
|
||||
export fn close(fd: i32) i32 = {
|
||||
return syscall1(SYS_CLOSE, fd: i64): i32;
|
||||
return syscall1(nr.CLOSE, fd: i64): i32;
|
||||
};
|
||||
|
||||
// dup2(2): make `newfd` refer to the same description as `oldfd`,
|
||||
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
|
||||
// negative errno. Used by w6c_ww to redirect stdout into an output
|
||||
// file without changing the cgen emit path.
|
||||
export fn dup2(oldfd: i32, newfd: i32) i32 = {
|
||||
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32;
|
||||
return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
|
||||
};
|
||||
|
||||
// Fallible wrappers. The error variant is `oserror` (an i64 carrying
|
||||
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
|
||||
// arena-copied paths usually are by construction). Returns -errno on
|
||||
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
|
||||
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
|
||||
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
|
||||
return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
|
||||
};
|
||||
|
||||
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
|
||||
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
|
||||
// a negative errno. We use this for fstat-free file-size discovery
|
||||
// (open ⇒ lseek to end ⇒ lseek back).
|
||||
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
|
||||
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
|
||||
return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
|
||||
};
|
||||
|
||||
// oserror — the underlying errno from a failed syscall, as a
|
||||
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
|
||||
// access(2): returns 0 if the file is reachable, negative errno
|
||||
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
|
||||
export fn access(path: *u8, mode: i32) i32 = {
|
||||
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32;
|
||||
return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
|
||||
};
|
||||
|
||||
// remove — unlink(2). Hare name; the underlying syscall is unlink(2).
|
||||
export fn remove(path: *u8) i32 = {
|
||||
return syscall1(SYS_UNLINK, path: i64): i32;
|
||||
return syscall1(nr.UNLINK, path: i64): i32;
|
||||
};
|
||||
|
||||
// getpid(2). Used by the driver to mint unique scratch paths.
|
||||
export fn getpid() i32 = {
|
||||
return syscall0(SYS_GETPID): i32;
|
||||
return syscall0(nr.GETPID): i32;
|
||||
};
|
||||
|
||||
// fork(2): 0 in the child, child pid in the parent, negative errno
|
||||
// on failure.
|
||||
export fn fork() i32 = {
|
||||
return syscall0(SYS_FORK): i32;
|
||||
return syscall0(nr.FORK): i32;
|
||||
};
|
||||
|
||||
// execve(2): on success, does not return.
|
||||
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
|
||||
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32;
|
||||
return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
|
||||
};
|
||||
|
||||
// wait4(2): wait for `pid` (or any child if -1), store status in
|
||||
// `*status`, return the pid that ended (or negative errno).
|
||||
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
|
||||
return syscall4(SYS_WAIT4, pid: i64, status: i64,
|
||||
return syscall4(nr.WAIT4, pid: i64, status: i64,
|
||||
options: i64, rusage: i64): i32;
|
||||
};
|
||||
|
||||
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
|
||||
// negative errno. The driver uses it to expand `.` to the cwd's
|
||||
// basename for `ww build` / `ww test`.
|
||||
export fn getcwd(buf: *u8, n: u64) i64 = {
|
||||
return syscall2(SYS_GETCWD, buf: i64, n: i64);
|
||||
return syscall2(nr.GETCWD, buf: i64, n: i64);
|
||||
};
|
||||
|
||||
// getdents64(2) — Linux directory enumeration. The fd must be opened
|
||||
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
|
||||
// Returns bytes written into `buf` (advance by d_reclen to walk),
|
||||
// 0 at end-of-directory, or a negative errno.
|
||||
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
|
||||
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64);
|
||||
return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
|
||||
};
|
||||
|
||||
// MODULE: wcc
|
||||
|
||||
Reference in New Issue
Block a user