github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/syscall/syscall_libc_wasip1.go (about)

     1  //go:build wasip1
     2  
     3  package syscall
     4  
     5  import (
     6  	"internal/itoa"
     7  	"unsafe"
     8  )
     9  
    10  // https://github.com/WebAssembly/wasi-libc/blob/main/expected/wasm32-wasi/predefined-macros.txt
    11  // disagrees with ../../lib/wasi-libc/libc-top-half/musl/arch/wasm32/bits/signal.h for SIGCHLD?
    12  // https://github.com/WebAssembly/wasi-libc/issues/271
    13  
    14  type Signal int
    15  
    16  const (
    17  	SIGINT  Signal = 2
    18  	SIGQUIT Signal = 3
    19  	SIGILL  Signal = 4
    20  	SIGTRAP Signal = 5
    21  	SIGABRT Signal = 6
    22  	SIGBUS  Signal = 7
    23  	SIGFPE  Signal = 8
    24  	SIGKILL Signal = 9
    25  	SIGSEGV Signal = 11
    26  	SIGPIPE Signal = 13
    27  	SIGTERM Signal = 15
    28  	SIGCHLD Signal = 17
    29  )
    30  
    31  func (s Signal) Signal() {}
    32  
    33  func (s Signal) String() string {
    34  	if 0 <= s && int(s) < len(signals) {
    35  		str := signals[s]
    36  		if str != "" {
    37  			return str
    38  		}
    39  	}
    40  	return "signal " + itoa.Itoa(int(s))
    41  }
    42  
    43  var signals = [...]string{}
    44  
    45  const (
    46  	Stdin  = 0
    47  	Stdout = 1
    48  	Stderr = 2
    49  )
    50  
    51  const (
    52  	__WASI_OFLAGS_CREAT     = 1
    53  	__WASI_OFLAGS_DIRECTORY = 2
    54  	__WASI_OFLAGS_EXCL      = 4
    55  	__WASI_OFLAGS_TRUNC     = 8
    56  
    57  	__WASI_FDFLAGS_APPEND   = 1
    58  	__WASI_FDFLAGS_DSYNC    = 2
    59  	__WASI_FDFLAGS_NONBLOCK = 4
    60  	__WASI_FDFLAGS_RSYNC    = 8
    61  	__WASI_FDFLAGS_SYNC     = 16
    62  
    63  	__WASI_FILETYPE_UNKNOWN          = 0
    64  	__WASI_FILETYPE_BLOCK_DEVICE     = 1
    65  	__WASI_FILETYPE_CHARACTER_DEVICE = 2
    66  	__WASI_FILETYPE_DIRECTORY        = 3
    67  	__WASI_FILETYPE_REGULAR_FILE     = 4
    68  	__WASI_FILETYPE_SOCKET_DGRAM     = 5
    69  	__WASI_FILETYPE_SOCKET_STREAM    = 6
    70  	__WASI_FILETYPE_SYMBOLIC_LINK    = 7
    71  
    72  	// ../../lib/wasi-libc/libc-bottom-half/headers/public/__header_fcntl.h
    73  	O_RDONLY = 0x04000000
    74  	O_WRONLY = 0x10000000
    75  	O_RDWR   = O_RDONLY | O_WRONLY
    76  
    77  	O_CREAT     = __WASI_OFLAGS_CREAT << 12
    78  	O_TRUNC     = __WASI_OFLAGS_TRUNC << 12
    79  	O_EXCL      = __WASI_OFLAGS_EXCL << 12
    80  	O_DIRECTORY = __WASI_OFLAGS_DIRECTORY << 12
    81  
    82  	O_APPEND   = __WASI_FDFLAGS_APPEND
    83  	O_DSYNC    = __WASI_FDFLAGS_DSYNC
    84  	O_NONBLOCK = __WASI_FDFLAGS_NONBLOCK
    85  	O_RSYNC    = __WASI_FDFLAGS_RSYNC
    86  	O_SYNC     = __WASI_FDFLAGS_SYNC
    87  
    88  	O_CLOEXEC = 0
    89  
    90  	// ../../lib/wasi-libc/sysroot/include/sys/mman.h
    91  	MAP_FILE      = 0
    92  	MAP_SHARED    = 0x01
    93  	MAP_PRIVATE   = 0x02
    94  	MAP_ANON      = 0x20
    95  	MAP_ANONYMOUS = MAP_ANON
    96  
    97  	// ../../lib/wasi-libc/sysroot/include/sys/mman.h
    98  	PROT_NONE  = 0
    99  	PROT_READ  = 1
   100  	PROT_WRITE = 2
   101  	PROT_EXEC  = 4
   102  
   103  	// ../../lib/wasi-libc/expected/wasm32-wasi/predefined-macros.txt
   104  	F_GETFL = 3
   105  	F_SETFL = 4
   106  )
   107  
   108  // These values are needed as a stub until Go supports WASI as a full target.
   109  // The constant values don't have a meaning and don't correspond to anything
   110  // real.
   111  const (
   112  	_ = iota
   113  	SYS_FCNTL
   114  	SYS_FCNTL64
   115  	SYS_FSTATAT64
   116  	SYS_OPENAT
   117  	SYS_UNLINKAT
   118  	PATH_MAX = 4096
   119  )
   120  
   121  //go:extern errno
   122  var libcErrno uintptr
   123  
   124  func getErrno() error {
   125  	return Errno(libcErrno)
   126  }
   127  
   128  func (e Errno) Is(target error) bool {
   129  	switch target.Error() {
   130  	case "permission denied":
   131  		return e == EACCES || e == EPERM || e == ENOTCAPABLE // ENOTCAPABLE is unique in WASI
   132  	case "file already exists":
   133  		return e == EEXIST || e == ENOTEMPTY
   134  	case "file does not exist":
   135  		return e == ENOENT
   136  	}
   137  	return false
   138  }
   139  
   140  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno.h
   141  const (
   142  	E2BIG           Errno = 1      /* Argument list too long */
   143  	EACCES          Errno = 2      /* Permission denied */
   144  	EADDRINUSE      Errno = 3      /* Address already in use */
   145  	EADDRNOTAVAIL   Errno = 4      /* Address not available */
   146  	EAFNOSUPPORT    Errno = 5      /* Address family not supported by protocol family */
   147  	EAGAIN          Errno = 6      /* Try again */
   148  	EWOULDBLOCK     Errno = EAGAIN /* Operation would block */
   149  	EALREADY        Errno = 7      /* Socket already connected */
   150  	EBADF           Errno = 8      /* Bad file number */
   151  	EBADMSG         Errno = 9      /* Trying to read unreadable message */
   152  	EBUSY           Errno = 10     /* Device or resource busy */
   153  	ECANCELED       Errno = 11     /* Operation canceled. */
   154  	ECHILD          Errno = 12     /* No child processes */
   155  	ECONNABORTED    Errno = 13     /* Connection aborted */
   156  	ECONNREFUSED    Errno = 14     /* Connection refused */
   157  	ECONNRESET      Errno = 15     /* Connection reset by peer */
   158  	EDEADLK         Errno = 16     /* Deadlock condition */
   159  	EDESTADDRREQ    Errno = 17     /* Destination address required */
   160  	EDOM            Errno = 18     /* Math arg out of domain of func */
   161  	EDQUOT          Errno = 19     /* Quota exceeded */
   162  	EEXIST          Errno = 20     /* File exists */
   163  	EFAULT          Errno = 21     /* Bad address */
   164  	EFBIG           Errno = 22     /* File too large */
   165  	EHOSTUNREACH    Errno = 23     /* Host is unreachable */
   166  	EIDRM           Errno = 24     /* Identifier removed */
   167  	EILSEQ          Errno = 25
   168  	EINPROGRESS     Errno = 26 /* Connection already in progress */
   169  	EINTR           Errno = 27 /* Interrupted system call */
   170  	EINVAL          Errno = 28 /* Invalid argument */
   171  	EIO             Errno = 29 /* I/O error */
   172  	EISCONN         Errno = 30 /* Socket is already connected */
   173  	EISDIR          Errno = 31 /* Is a directory */
   174  	ELOOP           Errno = 32 /* Too many symbolic links */
   175  	EMFILE          Errno = 33 /* Too many open files */
   176  	EMLINK          Errno = 34 /* Too many links */
   177  	EMSGSIZE        Errno = 35 /* Message too long */
   178  	EMULTIHOP       Errno = 36 /* Multihop attempted */
   179  	ENAMETOOLONG    Errno = 37 /* File name too long */
   180  	ENETDOWN        Errno = 38 /* Network interface is not configured */
   181  	ENETRESET       Errno = 39
   182  	ENETUNREACH     Errno = 40         /* Network is unreachable */
   183  	ENFILE          Errno = 41         /* File table overflow */
   184  	ENOBUFS         Errno = 42         /* No buffer space available */
   185  	ENODEV          Errno = 43         /* No such device */
   186  	ENOENT          Errno = 44         /* No such file or directory */
   187  	ENOEXEC         Errno = 45         /* Exec format error */
   188  	ENOLCK          Errno = 46         /* No record locks available */
   189  	ENOLINK         Errno = 47         /* The link has been severed */
   190  	ENOMEM          Errno = 48         /* Out of memory */
   191  	ENOMSG          Errno = 49         /* No message of desired type */
   192  	ENOPROTOOPT     Errno = 50         /* Protocol not available */
   193  	ENOSPC          Errno = 51         /* No space left on device */
   194  	ENOSYS          Errno = 52         /* Function not implemented */
   195  	ENOTCONN        Errno = 53         /* Socket is not connected */
   196  	ENOTDIR         Errno = 54         /* Not a directory */
   197  	ENOTEMPTY       Errno = 55         /* Directory not empty */
   198  	ENOTSOCK        Errno = 57         /* Socket operation on non-socket */
   199  	ESOCKTNOSUPPORT Errno = 58         /* Socket type not supported */
   200  	EOPNOTSUPP      Errno = 58         /* Operation not supported on transport endpoint */
   201  	ENOTSUP         Errno = EOPNOTSUPP /* Not supported */
   202  	ENOTTY          Errno = 59         /* Not a typewriter */
   203  	ENXIO           Errno = 60         /* No such device or address */
   204  	EOVERFLOW       Errno = 61         /* Value too large for defined data type */
   205  	EPERM           Errno = 63         /* Operation not permitted */
   206  	EPIPE           Errno = 64         /* Broken pipe */
   207  	EPROTO          Errno = 65         /* Protocol error */
   208  	EPROTONOSUPPORT Errno = 66         /* Unknown protocol */
   209  	EPROTOTYPE      Errno = 67         /* Protocol wrong type for socket */
   210  	ERANGE          Errno = 68         /* Math result not representable */
   211  	EROFS           Errno = 69         /* Read-only file system */
   212  	ESPIPE          Errno = 70         /* Illegal seek */
   213  	ESRCH           Errno = 71         /* No such process */
   214  	ESTALE          Errno = 72
   215  	ETIMEDOUT       Errno = 73 /* Connection timed out */
   216  	EXDEV           Errno = 75 /* Cross-device link */
   217  	ENOTCAPABLE     Errno = 76 /* Extension: Capabilities insufficient. */
   218  )
   219  
   220  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_timespec.h
   221  type Timespec struct {
   222  	Sec  int32
   223  	Nsec int64
   224  }
   225  
   226  // Unix returns the time stored in ts as seconds plus nanoseconds.
   227  func (ts *Timespec) Unix() (sec int64, nsec int64) {
   228  	return int64(ts.Sec), int64(ts.Nsec)
   229  }
   230  
   231  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_stat.h
   232  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__typedef_ino_t.h
   233  // etc.
   234  type Stat_t struct {
   235  	Dev       uint64
   236  	Ino       uint64
   237  	Nlink     uint64
   238  	Mode      uint32
   239  	Uid       uint32
   240  	Gid       uint32
   241  	Pad_cgo_0 [4]byte
   242  	Rdev      uint64
   243  	Size      int64
   244  	Blksize   int32
   245  	Blocks    int64
   246  
   247  	Atim   Timespec
   248  	Mtim   Timespec
   249  	Ctim   Timespec
   250  	Qspare [3]int64
   251  }
   252  
   253  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-top-half/musl/include/sys/stat.h
   254  const (
   255  	S_IFBLK  = 0x6000
   256  	S_IFCHR  = 0x2000
   257  	S_IFDIR  = 0x4000
   258  	S_IFIFO  = 0x1000
   259  	S_IFLNK  = 0xa000
   260  	S_IFMT   = 0xf000
   261  	S_IFREG  = 0x8000
   262  	S_IFSOCK = 0xc000
   263  	S_IREAD  = 0x100
   264  	S_IRGRP  = 0x20
   265  	S_IROTH  = 0x4
   266  	S_IRUSR  = 0x100
   267  	S_IRWXG  = 0x38
   268  	S_IRWXO  = 0x7
   269  	S_IRWXU  = 0x1c0
   270  	S_ISGID  = 0x400
   271  	S_ISUID  = 0x800
   272  	S_ISVTX  = 0x200
   273  	S_IWGRP  = 0x10
   274  	S_IWOTH  = 0x2
   275  	S_IWRITE = 0x80
   276  	S_IWUSR  = 0x80
   277  	S_IXGRP  = 0x8
   278  	S_IXOTH  = 0x1
   279  	S_IXUSR  = 0x40
   280  )
   281  
   282  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__header_dirent.h
   283  const (
   284  	DT_BLK     = __WASI_FILETYPE_BLOCK_DEVICE
   285  	DT_CHR     = __WASI_FILETYPE_CHARACTER_DEVICE
   286  	DT_DIR     = __WASI_FILETYPE_DIRECTORY
   287  	DT_FIFO    = __WASI_FILETYPE_SOCKET_STREAM
   288  	DT_LNK     = __WASI_FILETYPE_SYMBOLIC_LINK
   289  	DT_REG     = __WASI_FILETYPE_REGULAR_FILE
   290  	DT_UNKNOWN = __WASI_FILETYPE_UNKNOWN
   291  )
   292  
   293  // Dirent is returned by pointer from Readdir to iterate over directory entries.
   294  //
   295  // The pointer is managed by wasi-libc and is only valid until the next call to
   296  // Readdir or Fdclosedir.
   297  //
   298  // https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_dirent.h
   299  type Dirent struct {
   300  	Ino  uint64
   301  	Type uint8
   302  }
   303  
   304  func (dirent *Dirent) Name() []byte {
   305  	// The dirent C struct uses a flexible array member to indicate that the
   306  	// directory name is laid out in memory right after the struct data:
   307  	//
   308  	// struct dirent {
   309  	//   ino_t d_ino;
   310  	//   unsigned char d_type;
   311  	//   char d_name[];
   312  	// };
   313  	name := (*[PATH_MAX]byte)(unsafe.Add(unsafe.Pointer(dirent), 9))
   314  	for i, c := range name {
   315  		if c == 0 {
   316  			return name[:i:i]
   317  		}
   318  	}
   319  	return name[:]
   320  }
   321  
   322  func Fdopendir(fd int) (dir uintptr, err error) {
   323  	d := libc_fdopendir(int32(fd))
   324  
   325  	if d == nil {
   326  		err = getErrno()
   327  	}
   328  	return uintptr(d), err
   329  }
   330  
   331  func Fdclosedir(dir uintptr) (err error) {
   332  	// Unlike on other unix platform where only closedir exists, wasi-libc has
   333  	// fdclosedir which releases resources and returns the file descriptor but
   334  	// does not close it. This is useful for us since we want to be able to keep
   335  	// using it.
   336  	n := libc_fdclosedir(unsafe.Pointer(dir))
   337  
   338  	if n < 0 {
   339  		err = getErrno()
   340  	}
   341  	return
   342  }
   343  
   344  func Readdir(dir uintptr) (dirent *Dirent, err error) {
   345  	// There might be a leftover errno value in the global variable, so we have
   346  	// to clear it before calling readdir because we cannot know whether a nil
   347  	// return means that we reached EOF or that an error occured.
   348  	libcErrno = 0
   349  
   350  	dirent = libc_readdir(unsafe.Pointer(dir))
   351  
   352  	if dirent == nil && libcErrno != 0 {
   353  		err = getErrno()
   354  	}
   355  	return
   356  }
   357  
   358  func Stat(path string, p *Stat_t) (err error) {
   359  	data := cstring(path)
   360  	n := libc_stat(&data[0], unsafe.Pointer(p))
   361  
   362  	if n < 0 {
   363  		err = getErrno()
   364  	}
   365  	return
   366  }
   367  
   368  func Fstat(fd int, p *Stat_t) (err error) {
   369  	n := libc_fstat(int32(fd), unsafe.Pointer(p))
   370  
   371  	if n < 0 {
   372  		err = getErrno()
   373  	}
   374  	return
   375  }
   376  
   377  func Lstat(path string, p *Stat_t) (err error) {
   378  	data := cstring(path)
   379  	n := libc_lstat(&data[0], unsafe.Pointer(p))
   380  	if n < 0 {
   381  		err = getErrno()
   382  	}
   383  	return
   384  }
   385  
   386  func Pipe2(p []int, flags int) (err error) {
   387  	return ENOSYS // TODO
   388  }
   389  
   390  func Chmod(path string, mode uint32) (err error) {
   391  	// wasi does not have chmod, but there are tests that validate that calling
   392  	// os.Chmod does not error (e.g. io/fs.TestIssue51617).
   393  	//
   394  	// We make a call to Lstat instead so we detect conditions like the path not
   395  	// existing, but we don't honnor the request to modify the file permissions.
   396  	stat := Stat_t{}
   397  	return Lstat(path, &stat)
   398  }
   399  
   400  // TODO: should this return runtime.wasmPageSize?
   401  func Getpagesize() int {
   402  	return libc_getpagesize()
   403  }
   404  
   405  type Utsname struct {
   406  	Sysname    [65]int8
   407  	Nodename   [65]int8
   408  	Release    [65]int8
   409  	Version    [65]int8
   410  	Machine    [65]int8
   411  	Domainname [65]int8
   412  }
   413  
   414  // Stub Utsname, needed because WASI pretends to be linux/arm.
   415  func Uname(buf *Utsname) (err error)
   416  
   417  type RawSockaddrInet4 struct {
   418  	// stub
   419  }
   420  
   421  type RawSockaddrInet6 struct {
   422  	// stub
   423  }
   424  
   425  // This is a stub, it is not functional.
   426  func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
   427  
   428  // This is a stub, it is not functional.
   429  func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
   430  
   431  // int getpagesize(void);
   432  //
   433  //export getpagesize
   434  func libc_getpagesize() int
   435  
   436  // int stat(const char *path, struct stat * buf);
   437  //
   438  //export stat
   439  func libc_stat(pathname *byte, ptr unsafe.Pointer) int32
   440  
   441  // int fstat(int fd, struct stat * buf);
   442  //
   443  //export fstat
   444  func libc_fstat(fd int32, ptr unsafe.Pointer) int32
   445  
   446  // int lstat(const char *path, struct stat * buf);
   447  //
   448  //export lstat
   449  func libc_lstat(pathname *byte, ptr unsafe.Pointer) int32
   450  
   451  // int open(const char *pathname, int flags, mode_t mode);
   452  //
   453  //export open
   454  func libc_open(pathname *byte, flags int32, mode uint32) int32
   455  
   456  // DIR *fdopendir(int);
   457  //
   458  //export fdopendir
   459  func libc_fdopendir(fd int32) unsafe.Pointer
   460  
   461  // int fdclosedir(DIR *);
   462  //
   463  //export fdclosedir
   464  func libc_fdclosedir(unsafe.Pointer) int32
   465  
   466  // struct dirent *readdir(DIR *);
   467  //
   468  //export readdir
   469  func libc_readdir(unsafe.Pointer) *Dirent