diff --git a/.travis.yml b/.travis.yml index 995884d105..acec65141e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: go matrix: include: - os: osx - go: "1.11" + go: "1.12" env: PATH="/usr/local/opt/llvm/bin:$PATH" before_install: - mkdir -p /Users/travis/gopath/bin diff --git a/Makefile b/Makefile index 93db041200..e89c38f03b 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ clean: @rm -rf build fmt: - @go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/os ./src/runtime ./src/sync + @go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/os ./src/runtime ./src/sync ./src/syscall @go fmt ./testdata/*.go test: diff --git a/compiler/compiler.go b/compiler/compiler.go index 93b3f22456..13a29d4361 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -203,7 +203,7 @@ func (c *Compiler) Compile(mainPath string) error { return true } else if path == "syscall" { for _, tag := range c.BuildTags { - if tag == "avr" || tag == "cortexm" { + if tag == "avr" || tag == "cortexm" || tag == "darwin" { return true } } diff --git a/src/syscall/errno.go b/src/syscall/errno.go new file mode 100644 index 0000000000..b67a685c85 --- /dev/null +++ b/src/syscall/errno.go @@ -0,0 +1,30 @@ +package syscall + +// Most code here has been copied from the Go sources: +// https://github.com/golang/go/blob/go1.12/src/syscall/syscall_js.go +// It has the following copyright note: +// +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// An Errno is an unsigned number describing an error condition. +// It implements the error interface. The zero Errno is by convention +// a non-error, so code to convert from Errno to error should use: +// err = nil +// if errno != 0 { +// err = errno +// } +type Errno uintptr + +func (e Errno) Error() string { + return "errno " + itoa(int(e)) +} + +func (e Errno) Temporary() bool { + return e == EINTR || e == EMFILE || e.Timeout() +} + +func (e Errno) Timeout() bool { + return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT +} diff --git a/src/syscall/syscall_baremetal.go b/src/syscall/syscall_baremetal.go index 05b0e57db2..fe17fcabb4 100644 --- a/src/syscall/syscall_baremetal.go +++ b/src/syscall/syscall_baremetal.go @@ -1,3 +1,5 @@ +// +build avr cortexm + package syscall // Most code here has been copied from the Go sources: @@ -8,33 +10,6 @@ package syscall // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// An Errno is an unsigned number describing an error condition. -// It implements the error interface. The zero Errno is by convention -// a non-error, so code to convert from Errno to error should use: -// err = nil -// if errno != 0 { -// err = errno -// } -type Errno uintptr - -func (e Errno) Error() string { - if 0 <= int(e) && int(e) < len(errorstr) { - s := errorstr[e] - if s != "" { - return s - } - } - return "errno " + itoa(int(e)) -} - -func (e Errno) Temporary() bool { - return e == EINTR || e == EMFILE || e.Timeout() -} - -func (e Errno) Timeout() bool { - return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT -} - // A Signal is a number describing a process signal. // It implements the os.Signal interface. type Signal int diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go new file mode 100644 index 0000000000..27e73733df --- /dev/null +++ b/src/syscall/syscall_darwin.go @@ -0,0 +1,51 @@ +package syscall + +// This file defines errno and constants to match the darwin libsystem ABI. +// Values have been determined experimentally by compiling some C code on macOS +// with Clang and looking at the resulting LLVM IR. + +// This function returns the error location in the darwin ABI. +// Discovered by compiling the following code using Clang: +// +// #include +// int getErrno() { +// return errno; +// } +// +//go:export __error +func libc___error() *int32 + +// getErrno returns the current C errno. It may not have been caused by the last +// call, so it should only be relied upon when the last call indicates an error +// (for example, by returning -1). +func getErrno() Errno { + errptr := libc___error() + return Errno(uintptr(*errptr)) +} + +const ( + ENOENT Errno = 2 + EINTR Errno = 4 + EMFILE Errno = 24 + EAGAIN Errno = 35 + ETIMEDOUT Errno = 60 + ENOSYS Errno = 78 + EWOULDBLOCK Errno = EAGAIN +) + +type Signal int + +const ( + SIGCHLD Signal = 20 + SIGINT Signal = 2 + SIGKILL Signal = 9 + SIGTRAP Signal = 5 + SIGQUIT Signal = 3 + SIGTERM Signal = 15 +) + +const ( + O_RDONLY = 0 + O_WRONLY = 1 + O_RDWR = 2 +) diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go new file mode 100644 index 0000000000..c5fc4b28d6 --- /dev/null +++ b/src/syscall/syscall_libc.go @@ -0,0 +1,57 @@ +// +build darwin + +package syscall + +import ( + "unsafe" +) + +func Close(fd int) (err error) { + return ENOSYS // TODO +} + +func Write(fd int, p []byte) (n int, err error) { + buf, count := splitSlice(p) + n = libc_write(int32(fd), buf, uint(count)) + if n < 0 { + err = getErrno() + } + return +} + +func Read(fd int, p []byte) (n int, err error) { + return 0, ENOSYS // TODO +} + +func Seek(fd int, offset int64, whence int) (off int64, err error) { + return 0, ENOSYS // TODO +} + +func Open(path string, mode int, perm uint32) (fd int, err error) { + return 0, ENOSYS // TODO +} + +func Kill(pid int, sig Signal) (err error) { + return ENOSYS // TODO +} + +func Getpid() (pid int) { + panic("unimplemented: getpid") // TODO +} + +func Getenv(key string) (value string, found bool) { + return "", false // TODO +} + +func splitSlice(p []byte) (buf *byte, len uintptr) { + slice := (*struct { + buf *byte + len uintptr + cap uintptr + })(unsafe.Pointer(&p)) + return slice.buf, slice.len +} + +// ssize_t write(int fd, const void *buf, size_t count) +//go:export write +func libc_write(fd int32, buf *byte, count uint) int diff --git a/src/syscall/tables_baremetal.go b/src/syscall/tables_baremetal.go index 38b099f9fc..c10799c0dc 100644 --- a/src/syscall/tables_baremetal.go +++ b/src/syscall/tables_baremetal.go @@ -6,8 +6,6 @@ package syscall -import "runtime" - // TODO: generate with runtime/mknacl.sh, allow override with IRT. const ( sys_null = 1 @@ -223,123 +221,6 @@ const ( EWOULDBLOCK Errno = EAGAIN /* Operation would block */ ) -// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.) -var errorstr = [...]string{ - EPERM: "Operation not permitted", - ENOENT: "No such file or directory", - ESRCH: "No such process", - EINTR: "Interrupted system call", - EIO: "I/O error", - ENXIO: "No such device or address", - E2BIG: "Argument list too long", - ENOEXEC: "Exec format error", - EBADF: "Bad file number", - ECHILD: "No child processes", - EAGAIN: "Try again", - ENOMEM: "Out of memory", - EACCES: "Permission denied", - EFAULT: "Bad address", - EBUSY: "Device or resource busy", - EEXIST: "File exists", - EXDEV: "Cross-device link", - ENODEV: "No such device", - ENOTDIR: "Not a directory", - EISDIR: "Is a directory", - EINVAL: "Invalid argument", - ENFILE: "File table overflow", - EMFILE: "Too many open files", - ENOTTY: "Not a typewriter", - EFBIG: "File too large", - ENOSPC: "No space left on device", - ESPIPE: "Illegal seek", - EROFS: "Read-only file system", - EMLINK: "Too many links", - EPIPE: "Broken pipe", - ENAMETOOLONG: "File name too long", - ENOSYS: "not implemented on " + runtime.GOOS, - EDQUOT: "Quota exceeded", - EDOM: "Math arg out of domain of func", - ERANGE: "Math result not representable", - EDEADLK: "Deadlock condition", - ENOLCK: "No record locks available", - ENOTEMPTY: "Directory not empty", - ELOOP: "Too many symbolic links", - ENOMSG: "No message of desired type", - EIDRM: "Identifier removed", - ECHRNG: "Channel number out of range", - EL2NSYNC: "Level 2 not synchronized", - EL3HLT: "Level 3 halted", - EL3RST: "Level 3 reset", - ELNRNG: "Link number out of range", - EUNATCH: "Protocol driver not attached", - ENOCSI: "No CSI structure available", - EL2HLT: "Level 2 halted", - EBADE: "Invalid exchange", - EBADR: "Invalid request descriptor", - EXFULL: "Exchange full", - ENOANO: "No anode", - EBADRQC: "Invalid request code", - EBADSLT: "Invalid slot", - EBFONT: "Bad font file fmt", - ENOSTR: "Device not a stream", - ENODATA: "No data (for no delay io)", - ETIME: "Timer expired", - ENOSR: "Out of streams resources", - ENONET: "Machine is not on the network", - ENOPKG: "Package not installed", - EREMOTE: "The object is remote", - ENOLINK: "The link has been severed", - EADV: "Advertise error", - ESRMNT: "Srmount error", - ECOMM: "Communication error on send", - EPROTO: "Protocol error", - EMULTIHOP: "Multihop attempted", - EDOTDOT: "Cross mount point (not really error)", - EBADMSG: "Trying to read unreadable message", - EOVERFLOW: "Value too large for defined data type", - ENOTUNIQ: "Given log. name not unique", - EBADFD: "f.d. invalid for this operation", - EREMCHG: "Remote address changed", - ELIBACC: "Can't access a needed shared lib", - ELIBBAD: "Accessing a corrupted shared lib", - ELIBSCN: ".lib section in a.out corrupted", - ELIBMAX: "Attempting to link in too many libs", - ELIBEXEC: "Attempting to exec a shared library", - ENOTSOCK: "Socket operation on non-socket", - EDESTADDRREQ: "Destination address required", - EMSGSIZE: "Message too long", - EPROTOTYPE: "Protocol wrong type for socket", - ENOPROTOOPT: "Protocol not available", - EPROTONOSUPPORT: "Unknown protocol", - ESOCKTNOSUPPORT: "Socket type not supported", - EOPNOTSUPP: "Operation not supported on transport endpoint", - EPFNOSUPPORT: "Protocol family not supported", - EAFNOSUPPORT: "Address family not supported by protocol family", - EADDRINUSE: "Address already in use", - EADDRNOTAVAIL: "Address not available", - ENETDOWN: "Network interface is not configured", - ENETUNREACH: "Network is unreachable", - ECONNABORTED: "Connection aborted", - ECONNRESET: "Connection reset by peer", - ENOBUFS: "No buffer space available", - EISCONN: "Socket is already connected", - ENOTCONN: "Socket is not connected", - ESHUTDOWN: "Can't send after socket shutdown", - ETIMEDOUT: "Connection timed out", - ECONNREFUSED: "Connection refused", - EHOSTDOWN: "Host is down", - EHOSTUNREACH: "Host is unreachable", - EALREADY: "Socket already connected", - EINPROGRESS: "Connection already in progress", - ENOMEDIUM: "No medium (in tape drive)", - ECANCELED: "Operation canceled.", - ELBIN: "Inode is remote (not really error)", - EFTYPE: "Inappropriate file type or format", - ENMFILE: "No more files", - ENOSHARE: "No such host or network path", - ECASECLASH: "Filename exists with different case", -} - // Do the interface allocations only once for common // Errno values. var (