Source file src/runtime/os_wasip1.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build wasip1
     6  
     7  package runtime
     8  
     9  import "unsafe"
    10  
    11  // GOARCH=wasm currently has 64 bits pointers, but the WebAssembly host expects
    12  // pointers to be 32 bits so we use this type alias to represent pointers in
    13  // structs and arrays passed as arguments to WASI functions.
    14  //
    15  // Note that the use of an integer type prevents the compiler from tracking
    16  // pointers passed to WASI functions, so we must use KeepAlive to explicitly
    17  // retain the objects that could otherwise be reclaimed by the GC.
    18  type uintptr32 = uint32
    19  
    20  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-size-u32
    21  type size = uint32
    22  
    23  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-errno-variant
    24  type errno = uint32
    25  
    26  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-filesize-u64
    27  type filesize = uint64
    28  
    29  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-timestamp-u64
    30  type timestamp = uint64
    31  
    32  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-clockid-variant
    33  type clockid = uint32
    34  
    35  const (
    36  	clockRealtime  clockid = 0
    37  	clockMonotonic clockid = 1
    38  )
    39  
    40  // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-iovec-record
    41  type iovec struct {
    42  	buf    uintptr32
    43  	bufLen size
    44  }
    45  
    46  //go:wasmimport wasi_snapshot_preview1 proc_exit
    47  func exit(code int32)
    48  
    49  //go:wasmimport wasi_snapshot_preview1 args_get
    50  //go:noescape
    51  func args_get(argv, argvBuf unsafe.Pointer) errno
    52  
    53  //go:wasmimport wasi_snapshot_preview1 args_sizes_get
    54  //go:noescape
    55  func args_sizes_get(argc, argvBufLen unsafe.Pointer) errno
    56  
    57  //go:wasmimport wasi_snapshot_preview1 clock_time_get
    58  //go:noescape
    59  func clock_time_get(clock_id clockid, precision timestamp, time unsafe.Pointer) errno
    60  
    61  //go:wasmimport wasi_snapshot_preview1 environ_get
    62  //go:noescape
    63  func environ_get(environ, environBuf unsafe.Pointer) errno
    64  
    65  //go:wasmimport wasi_snapshot_preview1 environ_sizes_get
    66  //go:noescape
    67  func environ_sizes_get(environCount, environBufLen unsafe.Pointer) errno
    68  
    69  //go:wasmimport wasi_snapshot_preview1 fd_write
    70  //go:noescape
    71  func fd_write(fd int32, iovs unsafe.Pointer, iovsLen size, nwritten unsafe.Pointer) errno
    72  
    73  //go:wasmimport wasi_snapshot_preview1 random_get
    74  //go:noescape
    75  func random_get(buf unsafe.Pointer, bufLen size) errno
    76  
    77  type eventtype = uint8
    78  
    79  const (
    80  	eventtypeClock eventtype = iota
    81  	eventtypeFdRead
    82  	eventtypeFdWrite
    83  )
    84  
    85  type eventrwflags = uint16
    86  
    87  const (
    88  	fdReadwriteHangup eventrwflags = 1 << iota
    89  )
    90  
    91  type userdata = uint64
    92  
    93  // The go:wasmimport directive currently does not accept values of type uint16
    94  // in arguments or returns of the function signature. Most WASI imports return
    95  // an errno value, which we have to define as uint32 because of that limitation.
    96  // However, the WASI errno type is intended to be a 16 bits integer, and in the
    97  // event struct the error field should be of type errno. If we used the errno
    98  // type for the error field it would result in a mismatching field alignment and
    99  // struct size because errno is declared as a 32 bits type, so we declare the
   100  // error field as a plain uint16.
   101  type event struct {
   102  	userdata    userdata
   103  	error       uint16
   104  	typ         eventtype
   105  	fdReadwrite eventFdReadwrite
   106  }
   107  
   108  type eventFdReadwrite struct {
   109  	nbytes filesize
   110  	flags  eventrwflags
   111  }
   112  
   113  type subclockflags = uint16
   114  
   115  const (
   116  	subscriptionClockAbstime subclockflags = 1 << iota
   117  )
   118  
   119  type subscriptionClock struct {
   120  	id        clockid
   121  	timeout   timestamp
   122  	precision timestamp
   123  	flags     subclockflags
   124  }
   125  
   126  type subscriptionFdReadwrite struct {
   127  	fd int32
   128  }
   129  
   130  type subscription struct {
   131  	userdata userdata
   132  	u        subscriptionUnion
   133  }
   134  
   135  type subscriptionUnion [5]uint64
   136  
   137  func (u *subscriptionUnion) eventtype() *eventtype {
   138  	return (*eventtype)(unsafe.Pointer(&u[0]))
   139  }
   140  
   141  func (u *subscriptionUnion) subscriptionClock() *subscriptionClock {
   142  	return (*subscriptionClock)(unsafe.Pointer(&u[1]))
   143  }
   144  
   145  func (u *subscriptionUnion) subscriptionFdReadwrite() *subscriptionFdReadwrite {
   146  	return (*subscriptionFdReadwrite)(unsafe.Pointer(&u[1]))
   147  }
   148  
   149  //go:wasmimport wasi_snapshot_preview1 poll_oneoff
   150  //go:noescape
   151  func poll_oneoff(in, out unsafe.Pointer, nsubscriptions size, nevents unsafe.Pointer) errno
   152  
   153  func write1(fd uintptr, p unsafe.Pointer, n int32) int32 {
   154  	iov := iovec{
   155  		buf:    uintptr32(uintptr(p)),
   156  		bufLen: size(n),
   157  	}
   158  	var nwritten size
   159  	if fd_write(int32(fd), unsafe.Pointer(&iov), 1, unsafe.Pointer(&nwritten)) != 0 {
   160  		throw("fd_write failed")
   161  	}
   162  	return int32(nwritten)
   163  }
   164  
   165  func usleep(usec uint32) {
   166  	var in subscription
   167  	var out event
   168  	var nevents size
   169  
   170  	eventtype := in.u.eventtype()
   171  	*eventtype = eventtypeClock
   172  
   173  	subscription := in.u.subscriptionClock()
   174  	subscription.id = clockMonotonic
   175  	subscription.timeout = timestamp(usec) * 1e3
   176  	subscription.precision = 1e3
   177  
   178  	if poll_oneoff(unsafe.Pointer(&in), unsafe.Pointer(&out), 1, unsafe.Pointer(&nevents)) != 0 {
   179  		throw("wasi_snapshot_preview1.poll_oneoff")
   180  	}
   181  }
   182  
   183  func readRandom(r []byte) int {
   184  	if random_get(unsafe.Pointer(&r[0]), size(len(r))) != 0 {
   185  		return 0
   186  	}
   187  	return len(r)
   188  }
   189  
   190  func goenvs() {
   191  	// arguments
   192  	var argc size
   193  	var argvBufLen size
   194  	if args_sizes_get(unsafe.Pointer(&argc), unsafe.Pointer(&argvBufLen)) != 0 {
   195  		throw("args_sizes_get failed")
   196  	}
   197  
   198  	argslice = make([]string, argc)
   199  	if argc > 0 {
   200  		argv := make([]uintptr32, argc)
   201  		argvBuf := make([]byte, argvBufLen)
   202  		if args_get(unsafe.Pointer(&argv[0]), unsafe.Pointer(&argvBuf[0])) != 0 {
   203  			throw("args_get failed")
   204  		}
   205  
   206  		for i := range argslice {
   207  			start := argv[i] - uintptr32(uintptr(unsafe.Pointer(&argvBuf[0])))
   208  			end := start
   209  			for argvBuf[end] != 0 {
   210  				end++
   211  			}
   212  			argslice[i] = string(argvBuf[start:end])
   213  		}
   214  	}
   215  
   216  	// environment
   217  	var environCount size
   218  	var environBufLen size
   219  	if environ_sizes_get(unsafe.Pointer(&environCount), unsafe.Pointer(&environBufLen)) != 0 {
   220  		throw("environ_sizes_get failed")
   221  	}
   222  
   223  	envs = make([]string, environCount)
   224  	if environCount > 0 {
   225  		environ := make([]uintptr32, environCount)
   226  		environBuf := make([]byte, environBufLen)
   227  		if environ_get(unsafe.Pointer(&environ[0]), unsafe.Pointer(&environBuf[0])) != 0 {
   228  			throw("environ_get failed")
   229  		}
   230  
   231  		for i := range envs {
   232  			start := environ[i] - uintptr32(uintptr(unsafe.Pointer(&environBuf[0])))
   233  			end := start
   234  			for environBuf[end] != 0 {
   235  				end++
   236  			}
   237  			envs[i] = string(environBuf[start:end])
   238  		}
   239  	}
   240  }
   241  
   242  func walltime() (sec int64, nsec int32) {
   243  	return walltime1()
   244  }
   245  
   246  func walltime1() (sec int64, nsec int32) {
   247  	var time timestamp
   248  	if clock_time_get(clockRealtime, 0, unsafe.Pointer(&time)) != 0 {
   249  		throw("clock_time_get failed")
   250  	}
   251  	return int64(time / 1000000000), int32(time % 1000000000)
   252  }
   253  
   254  func nanotime1() int64 {
   255  	var time timestamp
   256  	if clock_time_get(clockMonotonic, 0, unsafe.Pointer(&time)) != 0 {
   257  		throw("clock_time_get failed")
   258  	}
   259  	return int64(time)
   260  }
   261  

View as plain text