//go:build wasi || wasip1 package syscall import ( "internal/itoa" "unsafe" ) // https://github.com/WebAssembly/wasi-libc/blob/main/expected/wasm32-wasi/predefined-macros.txt // disagrees with ../../lib/wasi-libc/libc-top-half/musl/arch/wasm32/bits/signal.h for SIGCHLD? // https://github.com/WebAssembly/wasi-libc/issues/271 type Signal int const ( SIGINT Signal = 2 SIGQUIT Signal = 3 SIGILL Signal = 4 SIGTRAP Signal = 5 SIGABRT Signal = 6 SIGBUS Signal = 7 SIGFPE Signal = 8 SIGKILL Signal = 9 SIGSEGV Signal = 11 SIGPIPE Signal = 13 SIGTERM Signal = 15 SIGCHLD Signal = 17 ) func (s Signal) Signal() {} func (s Signal) String() string { if 0 <= s && int(s) < len(signals) { str := signals[s] if str != "" { return str } } return "signal " + itoa.Itoa(int(s)) } var signals = [...]string{} const ( Stdin = 0 Stdout = 1 Stderr = 2 ) const ( __WASI_OFLAGS_CREAT = 1 __WASI_OFLAGS_DIRECTORY = 2 __WASI_OFLAGS_EXCL = 4 __WASI_OFLAGS_TRUNC = 8 __WASI_FDFLAGS_APPEND = 1 __WASI_FDFLAGS_DSYNC = 2 __WASI_FDFLAGS_NONBLOCK = 4 __WASI_FDFLAGS_RSYNC = 8 __WASI_FDFLAGS_SYNC = 16 __WASI_FILETYPE_UNKNOWN = 0 __WASI_FILETYPE_BLOCK_DEVICE = 1 __WASI_FILETYPE_CHARACTER_DEVICE = 2 __WASI_FILETYPE_DIRECTORY = 3 __WASI_FILETYPE_REGULAR_FILE = 4 __WASI_FILETYPE_SOCKET_DGRAM = 5 __WASI_FILETYPE_SOCKET_STREAM = 6 __WASI_FILETYPE_SYMBOLIC_LINK = 7 // ../../lib/wasi-libc/libc-bottom-half/headers/public/__header_fcntl.h O_RDONLY = 0x04000000 O_WRONLY = 0x10000000 O_RDWR = O_RDONLY | O_WRONLY O_CREAT = __WASI_OFLAGS_CREAT << 12 O_TRUNC = __WASI_OFLAGS_TRUNC << 12 O_EXCL = __WASI_OFLAGS_EXCL << 12 O_DIRECTORY = __WASI_OFLAGS_DIRECTORY << 12 O_APPEND = __WASI_FDFLAGS_APPEND O_DSYNC = __WASI_FDFLAGS_DSYNC O_NONBLOCK = __WASI_FDFLAGS_NONBLOCK O_RSYNC = __WASI_FDFLAGS_RSYNC O_SYNC = __WASI_FDFLAGS_SYNC O_CLOEXEC = 0 // ../../lib/wasi-libc/sysroot/include/sys/mman.h MAP_FILE = 0 MAP_SHARED = 0x01 MAP_PRIVATE = 0x02 MAP_ANON = 0x20 MAP_ANONYMOUS = MAP_ANON // ../../lib/wasi-libc/sysroot/include/sys/mman.h PROT_NONE = 0 PROT_READ = 1 PROT_WRITE = 2 PROT_EXEC = 4 // ../../lib/wasi-libc/expected/wasm32-wasi/predefined-macros.txt F_GETFL = 3 F_SETFL = 4 ) // These values are needed as a stub until Go supports WASI as a full target. // The constant values don't have a meaning and don't correspond to anything // real. const ( _ = iota SYS_FCNTL SYS_FCNTL64 SYS_FSTATAT64 SYS_OPENAT SYS_UNLINKAT PATH_MAX = 4096 ) //go:extern errno var libcErrno uintptr func getErrno() error { return Errno(libcErrno) } func (e Errno) Is(target error) bool { switch target.Error() { case "permission denied": return e == EACCES || e == EPERM || e == ENOTCAPABLE // ENOTCAPABLE is unique in WASI case "file already exists": return e == EEXIST || e == ENOTEMPTY case "file does not exist": return e == ENOENT } return false } // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno.h const ( E2BIG Errno = 1 /* Argument list too long */ EACCES Errno = 2 /* Permission denied */ EADDRINUSE Errno = 3 /* Address already in use */ EADDRNOTAVAIL Errno = 4 /* Address not available */ EAFNOSUPPORT Errno = 5 /* Address family not supported by protocol family */ EAGAIN Errno = 6 /* Try again */ EWOULDBLOCK Errno = EAGAIN /* Operation would block */ EALREADY Errno = 7 /* Socket already connected */ EBADF Errno = 8 /* Bad file number */ EBADMSG Errno = 9 /* Trying to read unreadable message */ EBUSY Errno = 10 /* Device or resource busy */ ECANCELED Errno = 11 /* Operation canceled. */ ECHILD Errno = 12 /* No child processes */ ECONNABORTED Errno = 13 /* Connection aborted */ ECONNREFUSED Errno = 14 /* Connection refused */ ECONNRESET Errno = 15 /* Connection reset by peer */ EDEADLK Errno = 16 /* Deadlock condition */ EDESTADDRREQ Errno = 17 /* Destination address required */ EDOM Errno = 18 /* Math arg out of domain of func */ EDQUOT Errno = 19 /* Quota exceeded */ EEXIST Errno = 20 /* File exists */ EFAULT Errno = 21 /* Bad address */ EFBIG Errno = 22 /* File too large */ EHOSTUNREACH Errno = 23 /* Host is unreachable */ EIDRM Errno = 24 /* Identifier removed */ EILSEQ Errno = 25 EINPROGRESS Errno = 26 /* Connection already in progress */ EINTR Errno = 27 /* Interrupted system call */ EINVAL Errno = 28 /* Invalid argument */ EIO Errno = 29 /* I/O error */ EISCONN Errno = 30 /* Socket is already connected */ EISDIR Errno = 31 /* Is a directory */ ELOOP Errno = 32 /* Too many symbolic links */ EMFILE Errno = 33 /* Too many open files */ EMLINK Errno = 34 /* Too many links */ EMSGSIZE Errno = 35 /* Message too long */ EMULTIHOP Errno = 36 /* Multihop attempted */ ENAMETOOLONG Errno = 37 /* File name too long */ ENETDOWN Errno = 38 /* Network interface is not configured */ ENETRESET Errno = 39 ENETUNREACH Errno = 40 /* Network is unreachable */ ENFILE Errno = 41 /* File table overflow */ ENOBUFS Errno = 42 /* No buffer space available */ ENODEV Errno = 43 /* No such device */ ENOENT Errno = 44 /* No such file or directory */ ENOEXEC Errno = 45 /* Exec format error */ ENOLCK Errno = 46 /* No record locks available */ ENOLINK Errno = 47 /* The link has been severed */ ENOMEM Errno = 48 /* Out of memory */ ENOMSG Errno = 49 /* No message of desired type */ ENOPROTOOPT Errno = 50 /* Protocol not available */ ENOSPC Errno = 51 /* No space left on device */ ENOSYS Errno = 52 /* Function not implemented */ ENOTCONN Errno = 53 /* Socket is not connected */ ENOTDIR Errno = 54 /* Not a directory */ ENOTEMPTY Errno = 55 /* Directory not empty */ ENOTSOCK Errno = 57 /* Socket operation on non-socket */ ESOCKTNOSUPPORT Errno = 58 /* Socket type not supported */ EOPNOTSUPP Errno = 58 /* Operation not supported on transport endpoint */ ENOTSUP Errno = EOPNOTSUPP /* Not supported */ ENOTTY Errno = 59 /* Not a typewriter */ ENXIO Errno = 60 /* No such device or address */ EOVERFLOW Errno = 61 /* Value too large for defined data type */ EPERM Errno = 63 /* Operation not permitted */ EPIPE Errno = 64 /* Broken pipe */ EPROTO Errno = 65 /* Protocol error */ EPROTONOSUPPORT Errno = 66 /* Unknown protocol */ EPROTOTYPE Errno = 67 /* Protocol wrong type for socket */ ERANGE Errno = 68 /* Math result not representable */ EROFS Errno = 69 /* Read-only file system */ ESPIPE Errno = 70 /* Illegal seek */ ESRCH Errno = 71 /* No such process */ ESTALE Errno = 72 ETIMEDOUT Errno = 73 /* Connection timed out */ EXDEV Errno = 75 /* Cross-device link */ ENOTCAPABLE Errno = 76 /* Extension: Capabilities insufficient. */ ) // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_timespec.h type Timespec struct { Sec int32 Nsec int64 } // Unix returns the time stored in ts as seconds plus nanoseconds. func (ts *Timespec) Unix() (sec int64, nsec int64) { return int64(ts.Sec), int64(ts.Nsec) } // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_stat.h // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__typedef_ino_t.h // etc. type Stat_t struct { Dev uint64 Ino uint64 Nlink uint64 Mode uint32 Uid uint32 Gid uint32 Pad_cgo_0 [4]byte Rdev uint64 Size int64 Blksize int32 Blocks int64 Atim Timespec Mtim Timespec Ctim Timespec Qspare [3]int64 } // https://github.com/WebAssembly/wasi-libc/blob/main/libc-top-half/musl/include/sys/stat.h const ( S_IFBLK = 0x6000 S_IFCHR = 0x2000 S_IFDIR = 0x4000 S_IFIFO = 0x1000 S_IFLNK = 0xa000 S_IFMT = 0xf000 S_IFREG = 0x8000 S_IFSOCK = 0xc000 S_IREAD = 0x100 S_IRGRP = 0x20 S_IROTH = 0x4 S_IRUSR = 0x100 S_IRWXG = 0x38 S_IRWXO = 0x7 S_IRWXU = 0x1c0 S_ISGID = 0x400 S_ISUID = 0x800 S_ISVTX = 0x200 S_IWGRP = 0x10 S_IWOTH = 0x2 S_IWRITE = 0x80 S_IWUSR = 0x80 S_IXGRP = 0x8 S_IXOTH = 0x1 S_IXUSR = 0x40 ) // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__header_dirent.h const ( DT_BLK = __WASI_FILETYPE_BLOCK_DEVICE DT_CHR = __WASI_FILETYPE_CHARACTER_DEVICE DT_DIR = __WASI_FILETYPE_DIRECTORY DT_FIFO = __WASI_FILETYPE_SOCKET_STREAM DT_LNK = __WASI_FILETYPE_SYMBOLIC_LINK DT_REG = __WASI_FILETYPE_REGULAR_FILE DT_UNKNOWN = __WASI_FILETYPE_UNKNOWN ) // Dirent is returned by pointer from Readdir to iterate over directory entries. // // The pointer is managed by wasi-libc and is only valid until the next call to // Readdir or Fdclosedir. // // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_dirent.h type Dirent struct { Ino uint64 Type uint8 } func (dirent *Dirent) Name() []byte { // The dirent C struct uses a flexible array member to indicate that the // directory name is laid out in memory right after the struct data: // // struct dirent { // ino_t d_ino; // unsigned char d_type; // char d_name[]; // }; name := (*[PATH_MAX]byte)(unsafe.Add(unsafe.Pointer(dirent), 9)) for i, c := range name { if c == 0 { return name[:i:i] } } return name[:] } func Fdopendir(fd int) (dir uintptr, err error) { d := libc_fdopendir(int32(fd)) if d == nil { err = getErrno() } return uintptr(d), err } func Fdclosedir(dir uintptr) (err error) { // Unlike on other unix platform where only closedir exists, wasi-libc has // fdclosedir which releases resources and returns the file descriptor but // does not close it. This is useful for us since we want to be able to keep // using it. n := libc_fdclosedir(unsafe.Pointer(dir)) if n < 0 { err = getErrno() } return } func Readdir(dir uintptr) (dirent *Dirent, err error) { // There might be a leftover errno value in the global variable, so we have // to clear it before calling readdir because we cannot know whether a nil // return means that we reached EOF or that an error occured. libcErrno = 0 dirent = libc_readdir(unsafe.Pointer(dir)) if dirent == nil && libcErrno != 0 { err = getErrno() } return } func Stat(path string, p *Stat_t) (err error) { data := cstring(path) n := libc_stat(&data[0], unsafe.Pointer(p)) if n < 0 { err = getErrno() } return } func Fstat(fd int, p *Stat_t) (err error) { n := libc_fstat(int32(fd), unsafe.Pointer(p)) if n < 0 { err = getErrno() } return } func Lstat(path string, p *Stat_t) (err error) { data := cstring(path) n := libc_lstat(&data[0], unsafe.Pointer(p)) if n < 0 { err = getErrno() } return } func Pipe2(p []int, flags int) (err error) { return ENOSYS // TODO } func Chmod(path string, mode uint32) (err error) { // wasi does not have chmod, but there are tests that validate that calling // os.Chmod does not error (e.g. io/fs.TestIssue51617). // // We make a call to Lstat instead so we detect conditions like the path not // existing, but we don't honnor the request to modify the file permissions. stat := Stat_t{} return Lstat(path, &stat) } func Getpagesize() int { // per upstream return 65536 } type Utsname struct { Sysname [65]int8 Nodename [65]int8 Release [65]int8 Version [65]int8 Machine [65]int8 Domainname [65]int8 } // Stub Utsname, needed because WASI pretends to be linux/arm. func Uname(buf *Utsname) (err error) type RawSockaddrInet4 struct { // stub } type RawSockaddrInet6 struct { // stub } // This is a stub, it is not functional. func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) // This is a stub, it is not functional. func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) // int stat(const char *path, struct stat * buf); // //export stat func libc_stat(pathname *byte, ptr unsafe.Pointer) int32 // int fstat(int fd, struct stat * buf); // //export fstat func libc_fstat(fd int32, ptr unsafe.Pointer) int32 // int lstat(const char *path, struct stat * buf); // //export lstat func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32 // int open(const char *pathname, int flags, mode_t mode); // //export open func libc_open(pathname *byte, flags int32, mode uint32) int32 // DIR *fdopendir(int); // //export fdopendir func libc_fdopendir(fd int32) unsafe.Pointer // int fdclosedir(DIR *); // //export fdclosedir func libc_fdclosedir(unsafe.Pointer) int32 // struct dirent *readdir(DIR *); // //export readdir func libc_readdir(unsafe.Pointer) *Dirent