From 9841ac4acd8f0f288c01d09c68a95baa8e6dd548 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Wed, 17 Feb 2021 23:50:53 +0900 Subject: [PATCH] WASI & darwin: support env variables based on libc Signed-off-by: Takeshi Yoneda --- main_test.go | 16 +++++- src/os/env.go | 9 +++- src/syscall/syscall_baremetal.go | 2 +- src/syscall/syscall_libc.go | 34 ++++++++++--- ...scall_darwin.go => syscall_libc_darwin.go} | 0 ...itch.go => syscall_libc_nintendoswitch.go} | 0 src/syscall/syscall_libc_wasi.go | 50 +++++++++++++++++++ testdata/libc/env.go | 14 ++++++ testdata/libc/env.txt | 2 + 9 files changed, 115 insertions(+), 12 deletions(-) rename src/syscall/{syscall_darwin.go => syscall_libc_darwin.go} (100%) rename src/syscall/{syscall_nintendoswitch.go => syscall_libc_nintendoswitch.go} (100%) create mode 100644 src/syscall/syscall_libc_wasi.go create mode 100644 testdata/libc/env.go create mode 100644 testdata/libc/env.txt diff --git a/main_test.go b/main_test.go index a7eca14f..31ee8c5d 100644 --- a/main_test.go +++ b/main_test.go @@ -58,6 +58,9 @@ func TestCompiler(t *testing.T) { if runtime.GOOS != "windows" { t.Run("Host", func(t *testing.T) { runPlatTests("", matches, t) + if runtime.GOOS == "darwin" { + runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...) + } }) } @@ -104,6 +107,7 @@ func TestCompiler(t *testing.T) { t.Run("WASI", func(t *testing.T) { runPlatTests("wasi", matches, t) + runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...) }) } } @@ -113,7 +117,6 @@ func runPlatTests(target string, matches []string, t *testing.T) { for _, path := range matches { path := path // redefine to avoid race condition - t.Run(filepath.Base(path), func(t *testing.T) { t.Parallel() runTest(path, target, t) @@ -133,7 +136,7 @@ func runBuild(src, out string, opts *compileopts.Options) error { return Build(src, out, opts) } -func runTest(path, target string, t *testing.T) { +func runTest(path, target string, t *testing.T, environmentVars ...string) { // Get the expected output for this test. txtpath := path[:len(path)-3] + ".txt" if path[len(path)-1] == os.PathSeparator { @@ -182,6 +185,7 @@ func runTest(path, target string, t *testing.T) { ranTooLong := false if target == "" { cmd = exec.Command(binary) + cmd.Env = append(cmd.Env, environmentVars...) } else { spec, err := compileopts.LoadTarget(target) if err != nil { @@ -193,6 +197,14 @@ func runTest(path, target string, t *testing.T) { args := append(spec.Emulator[1:], binary) cmd = exec.Command(spec.Emulator[0], args...) } + + if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" { + for _, v := range environmentVars { + cmd.Args = append(cmd.Args, "--env", v) + } + } else { + cmd.Env = append(cmd.Env, environmentVars...) + } } stdout := &bytes.Buffer{} cmd.Stdout = stdout diff --git a/src/os/env.go b/src/os/env.go index 694056a9..eac3072a 100644 --- a/src/os/env.go +++ b/src/os/env.go @@ -1,9 +1,14 @@ package os +import ( + "syscall" +) + func Getenv(key string) string { - return "" + v, _ := syscall.Getenv(key) + return v } func LookupEnv(key string) (string, bool) { - return "", false + return syscall.Getenv(key) } diff --git a/src/syscall/syscall_baremetal.go b/src/syscall/syscall_baremetal.go index 2331101b..28fa39ba 100644 --- a/src/syscall/syscall_baremetal.go +++ b/src/syscall/syscall_baremetal.go @@ -1,4 +1,4 @@ -// +build baremetal wasi +// +build baremetal package syscall diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 337a2f01..cf9efa0c 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -1,4 +1,4 @@ -// +build darwin nintendoswitch +// +build darwin nintendoswitch wasi package syscall @@ -6,6 +6,12 @@ import ( "unsafe" ) +type sliceHeader struct { + buf *byte + len uintptr + cap uintptr +} + func Close(fd int) (err error) { return ENOSYS // TODO } @@ -48,18 +54,32 @@ func Getpid() (pid int) { } func Getenv(key string) (value string, found bool) { - return "", false // TODO + data := append([]byte(key), 0) + raw := libc_getenv(&data[0]) + if raw == nil { + return "", false + } + + ptr := uintptr(unsafe.Pointer(raw)) + for size := uintptr(0); ; size++ { + v := *(*byte)(unsafe.Pointer(ptr)) + if v == 0 { + src := *(*[]byte)(unsafe.Pointer(&sliceHeader{buf: raw, len: size, cap: size})) + return string(src), true + } + ptr += unsafe.Sizeof(byte(0)) + } } func splitSlice(p []byte) (buf *byte, len uintptr) { - slice := (*struct { - buf *byte - len uintptr - cap uintptr - })(unsafe.Pointer(&p)) + slice := (*sliceHeader)(unsafe.Pointer(&p)) return slice.buf, slice.len } // ssize_t write(int fd, const void *buf, size_t count) //export write func libc_write(fd int32, buf *byte, count uint) int + +// char *getenv(const char *name); +//export getenv +func libc_getenv(name *byte) *byte diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_libc_darwin.go similarity index 100% rename from src/syscall/syscall_darwin.go rename to src/syscall/syscall_libc_darwin.go diff --git a/src/syscall/syscall_nintendoswitch.go b/src/syscall/syscall_libc_nintendoswitch.go similarity index 100% rename from src/syscall/syscall_nintendoswitch.go rename to src/syscall/syscall_libc_nintendoswitch.go diff --git a/src/syscall/syscall_libc_wasi.go b/src/syscall/syscall_libc_wasi.go new file mode 100644 index 00000000..e6bed08c --- /dev/null +++ b/src/syscall/syscall_libc_wasi.go @@ -0,0 +1,50 @@ +// +build wasi + +package syscall + +// https://github.com/WebAssembly/wasi-libc/blob/main/expected/wasm32-wasi/predefined-macros.txt + +type Signal int + +const ( + SIGCHLD = 16 + SIGINT = 2 + SIGKILL = 9 + SIGTRAP = 5 + SIGQUIT = 3 + SIGTERM = 15 +) + +const ( + Stdin = 0 + Stdout = 1 + Stderr = 2 +) + +const ( + __WASI_OFLAGS_CREAT = 1 + __WASI_FDFLAGS_APPEND = 1 + __WASI_OFLAGS_EXCL = 4 + __WASI_OFLAGS_TRUNC = 8 + __WASI_FDFLAGS_SYNC = 16 + + O_RDONLY = 0x04000000 + O_WRONLY = 0x10000000 + O_RDWR = O_RDONLY | O_WRONLY + + O_CREAT = __WASI_OFLAGS_CREAT << 12 + O_CREATE = O_CREAT + O_TRUNC = __WASI_OFLAGS_TRUNC << 12 + O_APPEND = __WASI_FDFLAGS_APPEND + O_EXCL = __WASI_OFLAGS_EXCL << 12 + O_SYNC = __WASI_FDFLAGS_SYNC + + O_CLOEXEC = 0 +) + +//go:extern errno +var libcErrno uintptr + +func getErrno() error { + return Errno(libcErrno) +} diff --git a/testdata/libc/env.go b/testdata/libc/env.go new file mode 100644 index 00000000..7aa5772c --- /dev/null +++ b/testdata/libc/env.go @@ -0,0 +1,14 @@ +package main + +import ( + "os" +) + +func main() { + println(os.Getenv("ENV1")) + v, ok := os.LookupEnv("ENV2") + if !ok { + panic("ENV2 not found") + } + println(v) +} diff --git a/testdata/libc/env.txt b/testdata/libc/env.txt new file mode 100644 index 00000000..d12475f4 --- /dev/null +++ b/testdata/libc/env.txt @@ -0,0 +1,2 @@ +VALUE1 +VALUE2