Skip to content

Commit 86f8778

Browse files
aykevldeadprogram
authored andcommitted
darwin: use custom syscall pkg that uses libsystem
Go 1.12 switched to using libSystem.dylib for system calls, because Apple recommends against doing direct system calls that Go 1.11 and earlier did. For more information, see: golang/go#17490 https://developer.apple.com/library/archive/qa/qa1118/_index.html While the old syscall package was relatively easy to support in TinyGo (just implement syscall.Syscall*), this got a whole lot harder with Go 1.12 as all syscalls now go through CGo magic to call the underlying libSystem functions. Therefore, this commit overrides the stdlib syscall package with a custom package that performs calls with libc (libSystem). This may be useful not just for darwin but for other platforms as well that do not place the stable ABI at the syscall boundary like Linux but at the libc boundary. Only a very minimal part of the syscall package has been implemented, to get the tests to pass. More calls can easily be added in the future.
1 parent 2523772 commit 86f8778

File tree

8 files changed

+143
-149
lines changed

8 files changed

+143
-149
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ language: go
33
matrix:
44
include:
55
- os: osx
6-
go: "1.11"
6+
go: "1.12"
77
env: PATH="/usr/local/opt/llvm/bin:$PATH"
88
before_install:
99
- mkdir -p /Users/travis/gopath/bin

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ clean:
8282
@rm -rf build
8383

8484
fmt:
85-
@go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/os ./src/runtime ./src/sync
85+
@go fmt . ./compiler ./interp ./loader ./ir ./src/device/arm ./src/examples/* ./src/machine ./src/os ./src/runtime ./src/sync ./src/syscall
8686
@go fmt ./testdata/*.go
8787

8888
test:

compiler/compiler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (c *Compiler) Compile(mainPath string) error {
203203
return true
204204
} else if path == "syscall" {
205205
for _, tag := range c.BuildTags {
206-
if tag == "avr" || tag == "cortexm" {
206+
if tag == "avr" || tag == "cortexm" || tag == "darwin" {
207207
return true
208208
}
209209
}

src/syscall/errno.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package syscall
2+
3+
// Most code here has been copied from the Go sources:
4+
// https://github.com/golang/go/blob/go1.12/src/syscall/syscall_js.go
5+
// It has the following copyright note:
6+
//
7+
// Copyright 2018 The Go Authors. All rights reserved.
8+
// Use of this source code is governed by a BSD-style
9+
// license that can be found in the LICENSE file.
10+
11+
// An Errno is an unsigned number describing an error condition.
12+
// It implements the error interface. The zero Errno is by convention
13+
// a non-error, so code to convert from Errno to error should use:
14+
// err = nil
15+
// if errno != 0 {
16+
// err = errno
17+
// }
18+
type Errno uintptr
19+
20+
func (e Errno) Error() string {
21+
return "errno " + itoa(int(e))
22+
}
23+
24+
func (e Errno) Temporary() bool {
25+
return e == EINTR || e == EMFILE || e.Timeout()
26+
}
27+
28+
func (e Errno) Timeout() bool {
29+
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
30+
}

src/syscall/syscall_baremetal.go

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// +build avr cortexm
2+
13
package syscall
24

35
// Most code here has been copied from the Go sources:
@@ -8,33 +10,6 @@ package syscall
810
// Use of this source code is governed by a BSD-style
911
// license that can be found in the LICENSE file.
1012

11-
// An Errno is an unsigned number describing an error condition.
12-
// It implements the error interface. The zero Errno is by convention
13-
// a non-error, so code to convert from Errno to error should use:
14-
// err = nil
15-
// if errno != 0 {
16-
// err = errno
17-
// }
18-
type Errno uintptr
19-
20-
func (e Errno) Error() string {
21-
if 0 <= int(e) && int(e) < len(errorstr) {
22-
s := errorstr[e]
23-
if s != "" {
24-
return s
25-
}
26-
}
27-
return "errno " + itoa(int(e))
28-
}
29-
30-
func (e Errno) Temporary() bool {
31-
return e == EINTR || e == EMFILE || e.Timeout()
32-
}
33-
34-
func (e Errno) Timeout() bool {
35-
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
36-
}
37-
3813
// A Signal is a number describing a process signal.
3914
// It implements the os.Signal interface.
4015
type Signal int

src/syscall/syscall_darwin.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package syscall
2+
3+
// This file defines errno and constants to match the darwin libsystem ABI.
4+
// Values have been determined experimentally by compiling some C code on macOS
5+
// with Clang and looking at the resulting LLVM IR.
6+
7+
// This function returns the error location in the darwin ABI.
8+
// Discovered by compiling the following code using Clang:
9+
//
10+
// #include <errno.h>
11+
// int getErrno() {
12+
// return errno;
13+
// }
14+
//
15+
//go:export __error
16+
func libc___error() *int32
17+
18+
// getErrno returns the current C errno. It may not have been caused by the last
19+
// call, so it should only be relied upon when the last call indicates an error
20+
// (for example, by returning -1).
21+
func getErrno() Errno {
22+
errptr := libc___error()
23+
return Errno(uintptr(*errptr))
24+
}
25+
26+
const (
27+
ENOENT Errno = 2
28+
EINTR Errno = 4
29+
EMFILE Errno = 24
30+
EAGAIN Errno = 35
31+
ETIMEDOUT Errno = 60
32+
ENOSYS Errno = 78
33+
EWOULDBLOCK Errno = EAGAIN
34+
)
35+
36+
type Signal int
37+
38+
const (
39+
SIGCHLD Signal = 20
40+
SIGINT Signal = 2
41+
SIGKILL Signal = 9
42+
SIGTRAP Signal = 5
43+
SIGQUIT Signal = 3
44+
SIGTERM Signal = 15
45+
)
46+
47+
const (
48+
O_RDONLY = 0
49+
O_WRONLY = 1
50+
O_RDWR = 2
51+
)

src/syscall/syscall_libc.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// +build darwin
2+
3+
package syscall
4+
5+
import (
6+
"unsafe"
7+
)
8+
9+
func Close(fd int) (err error) {
10+
return ENOSYS // TODO
11+
}
12+
13+
func Write(fd int, p []byte) (n int, err error) {
14+
buf, count := splitSlice(p)
15+
n = libc_write(int32(fd), buf, uint(count))
16+
if n < 0 {
17+
err = getErrno()
18+
}
19+
return
20+
}
21+
22+
func Read(fd int, p []byte) (n int, err error) {
23+
return 0, ENOSYS // TODO
24+
}
25+
26+
func Seek(fd int, offset int64, whence int) (off int64, err error) {
27+
return 0, ENOSYS // TODO
28+
}
29+
30+
func Open(path string, mode int, perm uint32) (fd int, err error) {
31+
return 0, ENOSYS // TODO
32+
}
33+
34+
func Kill(pid int, sig Signal) (err error) {
35+
return ENOSYS // TODO
36+
}
37+
38+
func Getpid() (pid int) {
39+
panic("unimplemented: getpid") // TODO
40+
}
41+
42+
func Getenv(key string) (value string, found bool) {
43+
return "", false // TODO
44+
}
45+
46+
func splitSlice(p []byte) (buf *byte, len uintptr) {
47+
slice := (*struct {
48+
buf *byte
49+
len uintptr
50+
cap uintptr
51+
})(unsafe.Pointer(&p))
52+
return slice.buf, slice.len
53+
}
54+
55+
// ssize_t write(int fd, const void *buf, size_t count)
56+
//go:export write
57+
func libc_write(fd int32, buf *byte, count uint) int

src/syscall/tables_baremetal.go

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
package syscall
88

9-
import "runtime"
10-
119
// TODO: generate with runtime/mknacl.sh, allow override with IRT.
1210
const (
1311
sys_null = 1
@@ -223,123 +221,6 @@ const (
223221
EWOULDBLOCK Errno = EAGAIN /* Operation would block */
224222
)
225223

226-
// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.)
227-
var errorstr = [...]string{
228-
EPERM: "Operation not permitted",
229-
ENOENT: "No such file or directory",
230-
ESRCH: "No such process",
231-
EINTR: "Interrupted system call",
232-
EIO: "I/O error",
233-
ENXIO: "No such device or address",
234-
E2BIG: "Argument list too long",
235-
ENOEXEC: "Exec format error",
236-
EBADF: "Bad file number",
237-
ECHILD: "No child processes",
238-
EAGAIN: "Try again",
239-
ENOMEM: "Out of memory",
240-
EACCES: "Permission denied",
241-
EFAULT: "Bad address",
242-
EBUSY: "Device or resource busy",
243-
EEXIST: "File exists",
244-
EXDEV: "Cross-device link",
245-
ENODEV: "No such device",
246-
ENOTDIR: "Not a directory",
247-
EISDIR: "Is a directory",
248-
EINVAL: "Invalid argument",
249-
ENFILE: "File table overflow",
250-
EMFILE: "Too many open files",
251-
ENOTTY: "Not a typewriter",
252-
EFBIG: "File too large",
253-
ENOSPC: "No space left on device",
254-
ESPIPE: "Illegal seek",
255-
EROFS: "Read-only file system",
256-
EMLINK: "Too many links",
257-
EPIPE: "Broken pipe",
258-
ENAMETOOLONG: "File name too long",
259-
ENOSYS: "not implemented on " + runtime.GOOS,
260-
EDQUOT: "Quota exceeded",
261-
EDOM: "Math arg out of domain of func",
262-
ERANGE: "Math result not representable",
263-
EDEADLK: "Deadlock condition",
264-
ENOLCK: "No record locks available",
265-
ENOTEMPTY: "Directory not empty",
266-
ELOOP: "Too many symbolic links",
267-
ENOMSG: "No message of desired type",
268-
EIDRM: "Identifier removed",
269-
ECHRNG: "Channel number out of range",
270-
EL2NSYNC: "Level 2 not synchronized",
271-
EL3HLT: "Level 3 halted",
272-
EL3RST: "Level 3 reset",
273-
ELNRNG: "Link number out of range",
274-
EUNATCH: "Protocol driver not attached",
275-
ENOCSI: "No CSI structure available",
276-
EL2HLT: "Level 2 halted",
277-
EBADE: "Invalid exchange",
278-
EBADR: "Invalid request descriptor",
279-
EXFULL: "Exchange full",
280-
ENOANO: "No anode",
281-
EBADRQC: "Invalid request code",
282-
EBADSLT: "Invalid slot",
283-
EBFONT: "Bad font file fmt",
284-
ENOSTR: "Device not a stream",
285-
ENODATA: "No data (for no delay io)",
286-
ETIME: "Timer expired",
287-
ENOSR: "Out of streams resources",
288-
ENONET: "Machine is not on the network",
289-
ENOPKG: "Package not installed",
290-
EREMOTE: "The object is remote",
291-
ENOLINK: "The link has been severed",
292-
EADV: "Advertise error",
293-
ESRMNT: "Srmount error",
294-
ECOMM: "Communication error on send",
295-
EPROTO: "Protocol error",
296-
EMULTIHOP: "Multihop attempted",
297-
EDOTDOT: "Cross mount point (not really error)",
298-
EBADMSG: "Trying to read unreadable message",
299-
EOVERFLOW: "Value too large for defined data type",
300-
ENOTUNIQ: "Given log. name not unique",
301-
EBADFD: "f.d. invalid for this operation",
302-
EREMCHG: "Remote address changed",
303-
ELIBACC: "Can't access a needed shared lib",
304-
ELIBBAD: "Accessing a corrupted shared lib",
305-
ELIBSCN: ".lib section in a.out corrupted",
306-
ELIBMAX: "Attempting to link in too many libs",
307-
ELIBEXEC: "Attempting to exec a shared library",
308-
ENOTSOCK: "Socket operation on non-socket",
309-
EDESTADDRREQ: "Destination address required",
310-
EMSGSIZE: "Message too long",
311-
EPROTOTYPE: "Protocol wrong type for socket",
312-
ENOPROTOOPT: "Protocol not available",
313-
EPROTONOSUPPORT: "Unknown protocol",
314-
ESOCKTNOSUPPORT: "Socket type not supported",
315-
EOPNOTSUPP: "Operation not supported on transport endpoint",
316-
EPFNOSUPPORT: "Protocol family not supported",
317-
EAFNOSUPPORT: "Address family not supported by protocol family",
318-
EADDRINUSE: "Address already in use",
319-
EADDRNOTAVAIL: "Address not available",
320-
ENETDOWN: "Network interface is not configured",
321-
ENETUNREACH: "Network is unreachable",
322-
ECONNABORTED: "Connection aborted",
323-
ECONNRESET: "Connection reset by peer",
324-
ENOBUFS: "No buffer space available",
325-
EISCONN: "Socket is already connected",
326-
ENOTCONN: "Socket is not connected",
327-
ESHUTDOWN: "Can't send after socket shutdown",
328-
ETIMEDOUT: "Connection timed out",
329-
ECONNREFUSED: "Connection refused",
330-
EHOSTDOWN: "Host is down",
331-
EHOSTUNREACH: "Host is unreachable",
332-
EALREADY: "Socket already connected",
333-
EINPROGRESS: "Connection already in progress",
334-
ENOMEDIUM: "No medium (in tape drive)",
335-
ECANCELED: "Operation canceled.",
336-
ELBIN: "Inode is remote (not really error)",
337-
EFTYPE: "Inappropriate file type or format",
338-
ENMFILE: "No more files",
339-
ENOSHARE: "No such host or network path",
340-
ECASECLASH: "Filename exists with different case",
341-
}
342-
343224
// Do the interface allocations only once for common
344225
// Errno values.
345226
var (

0 commit comments

Comments
 (0)