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