aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/syscall/syscall_libc_wasi.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/syscall/syscall_libc_wasi.go')
-rw-r--r--src/syscall/syscall_libc_wasi.go479
1 files changed, 479 insertions, 0 deletions
diff --git a/src/syscall/syscall_libc_wasi.go b/src/syscall/syscall_libc_wasi.go
new file mode 100644
index 000000000..2d83a8714
--- /dev/null
+++ b/src/syscall/syscall_libc_wasi.go
@@ -0,0 +1,479 @@
+//go:build wasip1 || wasip2
+
+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_NOFOLLOW = 0x01000000
+ 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
+)
+
+func getErrno() error {
+ // libcErrno is the errno from wasi-libc for wasip1 and the errno for libc_wasip2 for wasip2
+ return 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 */
+ ENOTRECOVERABLE Errno = 56 /* State not recoverable */
+ 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 */
+ ETXTBSY Errno = 74 /* Text file busy */
+ EXDEV Errno = 75 /* Cross-device link */
+ ENOTCAPABLE Errno = 76 /* Extension: Capabilities insufficient. */
+
+ EWASIERROR Errno = 255 /* Unknown WASI error */
+)
+
+// TODO(ydnar): remove Timespec for WASI Preview 2 (seconds is uint64).
+//
+// 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 occurred.
+ 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)
+}
+
+// TODO: should this return runtime.wasmPageSize?
+func Getpagesize() int {
+ return libc_getpagesize()
+}
+
+type Utsname struct {
+ Sysname [65]int8
+ Nodename [65]int8
+ Release [65]int8
+ Version [65]int8
+ Machine [65]int8
+ Domainname [65]int8
+}
+
+//go:linkname faccessat syscall.Faccessat
+func faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
+ return ENOSYS
+}
+
+// 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 getpagesize(void);
+//
+//export getpagesize
+func libc_getpagesize() int
+
+// 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