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:
2026-05-12 04:44:57 +09:00
parent b9443b1f33
commit fc49da44d8
7 changed files with 287 additions and 245 deletions

View File

@@ -2,11 +2,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;
@@ -18,21 +18,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
@@ -53,22 +59,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`,
@@ -76,7 +82,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
@@ -99,7 +105,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) = {
@@ -112,7 +118,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
@@ -163,34 +169,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;
};
@@ -199,7 +205,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
@@ -217,5 +223,5 @@ 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);
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: strconv