Skip to content

Commit

Permalink
syscall: split out env logic so wasip2 can do non-libc version
Browse files Browse the repository at this point in the history
  • Loading branch information
dgryski committed Dec 8, 2023
1 parent 98609c5 commit eeb8f09
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 56 deletions.
2 changes: 1 addition & 1 deletion compiler/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
Expand Down
59 changes: 59 additions & 0 deletions src/syscall/env_libc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build (darwin || nintendoswitch || wasi || wasip1) && !wasip2

package syscall

import (
"unsafe"
)

func Environ() []string {

// This function combines all the environment into a single allocation.
// While this optimizes for memory usage and garbage collector
// overhead, it does run the risk of potentially pinning a "large"
// allocation if a user holds onto a single environment variable or
// value. Having each variable be its own allocation would make the
// trade-off in the other direction.

// calculate total memory required
var length uintptr
var vars int
for environ := libc_environ; *environ != nil; {
length += libc_strlen(*environ)
vars++
environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
}

// allocate our backing slice for the strings
b := make([]byte, length)
// and the slice we're going to return
envs := make([]string, 0, vars)

// loop over the environment again, this time copying over the data to the backing slice
for environ := libc_environ; *environ != nil; {
length = libc_strlen(*environ)
// construct a Go string pointing at the libc-allocated environment variable data
var envVar string
rawEnvVar := (*struct {
ptr unsafe.Pointer
length uintptr
})(unsafe.Pointer(&envVar))
rawEnvVar.ptr = *environ
rawEnvVar.length = length
// pull off the number of bytes we need for this environment variable
var bs []byte
bs, b = b[:length], b[length:]
// copy over the bytes to the Go heap
copy(bs, envVar)
// convert trimmed slice to string
s := *(*string)(unsafe.Pointer(&bs))
// add s to our list of environment variables
envs = append(envs, s)
// environ++
environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
}
return envs
}

//go:extern environ
var libc_environ *unsafe.Pointer
51 changes: 51 additions & 0 deletions src/syscall/env_wasip2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//go:build wasip2

package syscall

import (
"unsafe"
)

type __wasi_list_tuple struct {
ptr unsafe.Pointer
len uint32
}

type __wasi_tuple_string_string struct {
first __wasi_string
second __wasi_string
}

type __wasi_string struct {
data unsafe.Pointer
len uint32
}

type _string struct {
ptr *byte
length uintptr
}

//go:wasmimport wasi:cli/[email protected] get-environment
func __wasi_cli_environment_get_environment() __wasi_list_tuple

func Environ() []string {
var envs []string
list_tuple := __wasi_cli_environment_get_environment()
envs = make([]string, list_tuple.len)
ptr := list_tuple.ptr
for i := uint32(0); i < list_tuple.len; i++ {
tuple := *(*__wasi_tuple_string_string)(ptr)
first, second := tuple.first, tuple.second
envKey := _string{
(*byte)(first.data), uintptr(first.len),
}
envValue := _string{
(*byte)(second.data), uintptr(second.len),
}
envs[i] = *(*string)(unsafe.Pointer(&envKey)) + "=" + *(*string)(unsafe.Pointer(&envValue))
ptr = unsafe.Add(ptr, unsafe.Sizeof(__wasi_tuple_string_string{}))
}

return envs
}
3 changes: 0 additions & 3 deletions src/syscall/libc_wasip2.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,6 @@ func unlink(pathname *byte) int32 {
return 0
}

//go:export environ
var environ *unsafe.Pointer

// int getpagesize(void);
//
//go:export getpagesize
Expand Down
52 changes: 0 additions & 52 deletions src/syscall/syscall_libc.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,55 +250,6 @@ func Mprotect(b []byte, prot int) (err error) {
return
}

func Environ() []string {

// This function combines all the environment into a single allocation.
// While this optimizes for memory usage and garbage collector
// overhead, it does run the risk of potentially pinning a "large"
// allocation if a user holds onto a single environment variable or
// value. Having each variable be its own allocation would make the
// trade-off in the other direction.

// calculate total memory required
var length uintptr
var vars int
for environ := libc_environ; *environ != nil; {
length += libc_strlen(*environ)
vars++
environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
}

// allocate our backing slice for the strings
b := make([]byte, length)
// and the slice we're going to return
envs := make([]string, 0, vars)

// loop over the environment again, this time copying over the data to the backing slice
for environ := libc_environ; *environ != nil; {
length = libc_strlen(*environ)
// construct a Go string pointing at the libc-allocated environment variable data
var envVar string
rawEnvVar := (*struct {
ptr unsafe.Pointer
length uintptr
})(unsafe.Pointer(&envVar))
rawEnvVar.ptr = *environ
rawEnvVar.length = length
// pull off the number of bytes we need for this environment variable
var bs []byte
bs, b = b[:length], b[length:]
// copy over the bytes to the Go heap
copy(bs, envVar)
// convert trimmed slice to string
s := *(*string)(unsafe.Pointer(&bs))
// add s to our list of environment variables
envs = append(envs, s)
// environ++
environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
}
return envs
}

// BytePtrFromString returns a pointer to a NUL-terminated array of
// bytes containing the text of s. If s contains a NUL byte at any
// location, it returns (nil, EINVAL).
Expand Down Expand Up @@ -430,6 +381,3 @@ func libc_readlink(path *byte, buf *byte, count uint) int
//
//export unlink
func libc_unlink(pathname *byte) int32

//go:extern environ
var libc_environ *unsafe.Pointer

0 comments on commit eeb8f09

Please sign in to comment.