Skip to content

Commit 45d26eb

Browse files
committed
unix: convert Darwin syscalls from raw to libSystem
Call into libSystem (Darwin's libc equivalent) to do system calls. Raw syscalls are not a supported ABI on Darwin, they want us to use libSystem calls instead. The stdlib has already been converted to this new regime. This package is vendored into the stdlib, so we need to fix it also, then vendor it in again. Because this package isn't tied to a Go release, we need to keep the old code around and use the new code only for 1.12 and up. Update golang/go#17490 Change-Id: Idbcacff096b5bfeac871aa75dfd971570ac93322 Reviewed-on: https://go-review.googlesource.com/c/154179 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 97b4c2a commit 45d26eb

18 files changed

+10701
-8
lines changed

unix/darwin_test.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Copyright 2018 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+
// +build darwin,go1.12,amd64 darwin,go1.12,386
6+
7+
package unix
8+
9+
import (
10+
"os"
11+
"os/exec"
12+
"strings"
13+
"testing"
14+
)
15+
16+
type darwinTest struct {
17+
name string
18+
f func()
19+
}
20+
21+
// TODO(khr): decide whether to keep this test enabled permanently or
22+
// only temporarily.
23+
func TestDarwinLoader(t *testing.T) {
24+
// Make sure the Darwin dynamic loader can actually resolve
25+
// all the system calls into libSystem.dylib. Unfortunately
26+
// there is no easy way to test this at compile time. So we
27+
// implement a crazy hack here, calling into the syscall
28+
// function with all its arguments set to junk, and see what
29+
// error we get. We are happy with any error (or none) except
30+
// an error from the dynamic loader.
31+
//
32+
// We have to run each test in a separate subprocess for fault isolation.
33+
//
34+
// Hopefully the junk args won't accidentally ask the system to do "rm -fr /".
35+
//
36+
// In an ideal world each syscall would have its own test, so this test
37+
// would be unnecessary. Unfortunately, we do not live in that world.
38+
for _, test := range darwinTests {
39+
// Call the test binary recursively, giving it a magic argument
40+
// (see init below) and the name of the test to run.
41+
cmd := exec.Command(os.Args[0], "testDarwinLoader", test.name)
42+
43+
// Run subprocess, collect results. Note that we expect the subprocess
44+
// to fail somehow, so the error is irrelevant.
45+
out, _ := cmd.CombinedOutput()
46+
47+
if strings.Contains(string(out), "dyld: Symbol not found:") {
48+
t.Errorf("can't resolve %s in libSystem.dylib", test.name)
49+
}
50+
if !strings.Contains(string(out), "success") {
51+
// Not really an error. Might be a syscall that never returns,
52+
// like exit, or one that segfaults, like gettimeofday.
53+
t.Logf("test never finished: %s: %s", test.name, string(out))
54+
}
55+
}
56+
}
57+
58+
func init() {
59+
// The test binary execs itself with the "testDarwinLoader" argument.
60+
// Run the test specified by os.Args[2], then panic.
61+
if len(os.Args) >= 3 && os.Args[1] == "testDarwinLoader" {
62+
for _, test := range darwinTests {
63+
if test.name == os.Args[2] {
64+
test.f()
65+
}
66+
}
67+
// Panic with a "success" label, so the parent process can check it.
68+
panic("success")
69+
}
70+
}
71+
72+
// All the _trampoline functions in zsyscall_darwin_$ARCH.1_12.s
73+
var darwinTests = [...]darwinTest{
74+
{"getgroups", libc_getgroups_trampoline},
75+
{"setgroups", libc_setgroups_trampoline},
76+
{"wait4", libc_wait4_trampoline},
77+
{"accept", libc_accept_trampoline},
78+
{"bind", libc_bind_trampoline},
79+
{"connect", libc_connect_trampoline},
80+
{"socket", libc_socket_trampoline},
81+
{"getsockopt", libc_getsockopt_trampoline},
82+
{"setsockopt", libc_setsockopt_trampoline},
83+
{"getpeername", libc_getpeername_trampoline},
84+
{"getsockname", libc_getsockname_trampoline},
85+
{"shutdown", libc_shutdown_trampoline},
86+
{"socketpair", libc_socketpair_trampoline},
87+
{"recvfrom", libc_recvfrom_trampoline},
88+
{"sendto", libc_sendto_trampoline},
89+
{"recvmsg", libc_recvmsg_trampoline},
90+
{"sendmsg", libc_sendmsg_trampoline},
91+
{"kevent", libc_kevent_trampoline},
92+
{"__sysctl", libc___sysctl_trampoline},
93+
{"utimes", libc_utimes_trampoline},
94+
{"futimes", libc_futimes_trampoline},
95+
{"fcntl", libc_fcntl_trampoline},
96+
{"poll", libc_poll_trampoline},
97+
{"madvise", libc_madvise_trampoline},
98+
{"mlock", libc_mlock_trampoline},
99+
{"mlockall", libc_mlockall_trampoline},
100+
{"mprotect", libc_mprotect_trampoline},
101+
{"msync", libc_msync_trampoline},
102+
{"munlock", libc_munlock_trampoline},
103+
{"munlockall", libc_munlockall_trampoline},
104+
{"ptrace", libc_ptrace_trampoline},
105+
{"pipe", libc_pipe_trampoline},
106+
{"getxattr", libc_getxattr_trampoline},
107+
{"fgetxattr", libc_fgetxattr_trampoline},
108+
{"setxattr", libc_setxattr_trampoline},
109+
{"fsetxattr", libc_fsetxattr_trampoline},
110+
{"removexattr", libc_removexattr_trampoline},
111+
{"fremovexattr", libc_fremovexattr_trampoline},
112+
{"listxattr", libc_listxattr_trampoline},
113+
{"flistxattr", libc_flistxattr_trampoline},
114+
{"kill", libc_kill_trampoline},
115+
{"ioctl", libc_ioctl_trampoline},
116+
{"access", libc_access_trampoline},
117+
{"adjtime", libc_adjtime_trampoline},
118+
{"chdir", libc_chdir_trampoline},
119+
{"chflags", libc_chflags_trampoline},
120+
{"chmod", libc_chmod_trampoline},
121+
{"chown", libc_chown_trampoline},
122+
{"chroot", libc_chroot_trampoline},
123+
{"close", libc_close_trampoline},
124+
{"dup", libc_dup_trampoline},
125+
{"dup2", libc_dup2_trampoline},
126+
{"exchangedata", libc_exchangedata_trampoline},
127+
{"exit", libc_exit_trampoline},
128+
{"faccessat", libc_faccessat_trampoline},
129+
{"fchdir", libc_fchdir_trampoline},
130+
{"fchflags", libc_fchflags_trampoline},
131+
{"fchmod", libc_fchmod_trampoline},
132+
{"fchmodat", libc_fchmodat_trampoline},
133+
{"fchown", libc_fchown_trampoline},
134+
{"fchownat", libc_fchownat_trampoline},
135+
{"flock", libc_flock_trampoline},
136+
{"fpathconf", libc_fpathconf_trampoline},
137+
{"fstat64", libc_fstat64_trampoline},
138+
{"fstatat64", libc_fstatat64_trampoline},
139+
{"fstatfs64", libc_fstatfs64_trampoline},
140+
{"fsync", libc_fsync_trampoline},
141+
{"ftruncate", libc_ftruncate_trampoline},
142+
{"__getdirentries64", libc___getdirentries64_trampoline},
143+
{"getdtablesize", libc_getdtablesize_trampoline},
144+
{"getegid", libc_getegid_trampoline},
145+
{"geteuid", libc_geteuid_trampoline},
146+
{"getgid", libc_getgid_trampoline},
147+
{"getpgid", libc_getpgid_trampoline},
148+
{"getpgrp", libc_getpgrp_trampoline},
149+
{"getpid", libc_getpid_trampoline},
150+
{"getppid", libc_getppid_trampoline},
151+
{"getpriority", libc_getpriority_trampoline},
152+
{"getrlimit", libc_getrlimit_trampoline},
153+
{"getrusage", libc_getrusage_trampoline},
154+
{"getsid", libc_getsid_trampoline},
155+
{"getuid", libc_getuid_trampoline},
156+
{"issetugid", libc_issetugid_trampoline},
157+
{"kqueue", libc_kqueue_trampoline},
158+
{"lchown", libc_lchown_trampoline},
159+
{"link", libc_link_trampoline},
160+
{"linkat", libc_linkat_trampoline},
161+
{"listen", libc_listen_trampoline},
162+
{"lstat64", libc_lstat64_trampoline},
163+
{"mkdir", libc_mkdir_trampoline},
164+
{"mkdirat", libc_mkdirat_trampoline},
165+
{"mkfifo", libc_mkfifo_trampoline},
166+
{"mknod", libc_mknod_trampoline},
167+
{"open", libc_open_trampoline},
168+
{"openat", libc_openat_trampoline},
169+
{"pathconf", libc_pathconf_trampoline},
170+
{"pread", libc_pread_trampoline},
171+
{"pwrite", libc_pwrite_trampoline},
172+
{"read", libc_read_trampoline},
173+
{"readlink", libc_readlink_trampoline},
174+
{"readlinkat", libc_readlinkat_trampoline},
175+
{"rename", libc_rename_trampoline},
176+
{"renameat", libc_renameat_trampoline},
177+
{"revoke", libc_revoke_trampoline},
178+
{"rmdir", libc_rmdir_trampoline},
179+
{"lseek", libc_lseek_trampoline},
180+
{"select", libc_select_trampoline},
181+
{"setegid", libc_setegid_trampoline},
182+
{"seteuid", libc_seteuid_trampoline},
183+
{"setgid", libc_setgid_trampoline},
184+
{"setlogin", libc_setlogin_trampoline},
185+
{"setpgid", libc_setpgid_trampoline},
186+
{"setpriority", libc_setpriority_trampoline},
187+
{"setprivexec", libc_setprivexec_trampoline},
188+
{"setregid", libc_setregid_trampoline},
189+
{"setreuid", libc_setreuid_trampoline},
190+
{"setrlimit", libc_setrlimit_trampoline},
191+
{"setsid", libc_setsid_trampoline},
192+
{"settimeofday", libc_settimeofday_trampoline},
193+
{"setuid", libc_setuid_trampoline},
194+
{"stat64", libc_stat64_trampoline},
195+
{"statfs64", libc_statfs64_trampoline},
196+
{"symlink", libc_symlink_trampoline},
197+
{"symlinkat", libc_symlinkat_trampoline},
198+
{"sync", libc_sync_trampoline},
199+
{"truncate", libc_truncate_trampoline},
200+
{"umask", libc_umask_trampoline},
201+
{"undelete", libc_undelete_trampoline},
202+
{"unlink", libc_unlink_trampoline},
203+
{"unlinkat", libc_unlinkat_trampoline},
204+
{"unmount", libc_unmount_trampoline},
205+
{"write", libc_write_trampoline},
206+
{"mmap", libc_mmap_trampoline},
207+
{"munmap", libc_munmap_trampoline},
208+
{"gettimeofday", libc_gettimeofday_trampoline},
209+
}

unix/mkall.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mksysctl=""
1717
zsysctl="zsysctl_$GOOSARCH.go"
1818
mksysnum=
1919
mktypes=
20+
mkasm=
2021
run="sh"
2122
cmd=""
2223

@@ -74,21 +75,26 @@ darwin_386)
7475
mksyscall="go run mksyscall.go -l32"
7576
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
7677
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
78+
mkasm="go run mkasm_darwin.go"
7779
;;
7880
darwin_amd64)
7981
mkerrors="$mkerrors -m64"
8082
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
8183
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
84+
mkasm="go run mkasm_darwin.go"
8285
;;
8386
darwin_arm)
8487
mkerrors="$mkerrors"
88+
mksyscall="go run mksyscall.go -l32"
8589
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h"
8690
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
91+
mkasm="go run mkasm_darwin.go"
8792
;;
8893
darwin_arm64)
8994
mkerrors="$mkerrors -m64"
9095
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk iphoneos)/usr/include/sys/syscall.h"
9196
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
97+
mkasm="go run mkasm_darwin.go"
9298
;;
9399
dragonfly_amd64)
94100
mkerrors="$mkerrors -m64"
@@ -191,6 +197,11 @@ esac
191197
if [ "$GOOSARCH" == "aix_ppc64" ]; then
192198
# aix/ppc64 script generates files instead of writing to stdin.
193199
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
200+
elif [ "$GOOS" == "darwin" ]; then
201+
# pre-1.12, direct syscalls
202+
echo "$mksyscall -tags $GOOS,$GOARCH,!go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
203+
# 1.12 and later, syscalls via libSystem
204+
echo "$mksyscall -tags $GOOS,$GOARCH,go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.1_12.go";
194205
else
195206
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
196207
fi
@@ -200,5 +211,6 @@ esac
200211
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
201212
if [ -n "$mktypes" ]; then
202213
echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go";
214+
if [ -n "$mkasm" ]; then echo "$mkasm $GOARCH"; fi
203215
fi
204216
) | $run

unix/mkasm_darwin.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2018 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+
// +build ignore
6+
7+
// mkasm_darwin.go generates assembly trampolines to call libSystem routines from Go.
8+
//This program must be run after mksyscall.go.
9+
package main
10+
11+
import (
12+
"bytes"
13+
"fmt"
14+
"io/ioutil"
15+
"log"
16+
"os"
17+
"strings"
18+
)
19+
20+
func main() {
21+
in1, err := ioutil.ReadFile("syscall_darwin.go")
22+
if err != nil {
23+
log.Fatalf("can't open syscall_darwin.go: %s", err)
24+
}
25+
arch := os.Args[1]
26+
in2, err := ioutil.ReadFile(fmt.Sprintf("syscall_darwin_%s.go", arch))
27+
if err != nil {
28+
log.Fatalf("can't open syscall_darwin_%s.go: %s", arch, err)
29+
}
30+
in3, err := ioutil.ReadFile(fmt.Sprintf("zsyscall_darwin_%s.1_12.go", arch))
31+
if err != nil {
32+
log.Fatalf("can't open zsyscall_darwin_%s.1_12.go: %s", arch, err)
33+
}
34+
in := string(in1) + string(in2) + string(in3)
35+
36+
trampolines := map[string]bool{}
37+
38+
var out bytes.Buffer
39+
40+
fmt.Fprintf(&out, "// go run mkasm_darwin.go %s\n", strings.Join(os.Args[1:], " "))
41+
fmt.Fprintf(&out, "// Code generated by the command above; DO NOT EDIT.\n")
42+
fmt.Fprintf(&out, "\n")
43+
fmt.Fprintf(&out, "// +build go1.12\n")
44+
fmt.Fprintf(&out, "\n")
45+
fmt.Fprintf(&out, "#include \"textflag.h\"\n")
46+
for _, line := range strings.Split(in, "\n") {
47+
if !strings.HasPrefix(line, "func ") || !strings.HasSuffix(line, "_trampoline()") {
48+
continue
49+
}
50+
fn := line[5 : len(line)-13]
51+
if !trampolines[fn] {
52+
trampolines[fn] = true
53+
fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn)
54+
fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn)
55+
}
56+
}
57+
err = ioutil.WriteFile(fmt.Sprintf("zsyscall_darwin_%s.s", arch), out.Bytes(), 0644)
58+
if err != nil {
59+
log.Fatalf("can't write zsyscall_darwin_%s.s: %s", arch, err)
60+
}
61+
}

unix/mksyscall.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ func main() {
116116
endianness = "little-endian"
117117
}
118118

119+
libc := false
120+
if goos == "darwin" && strings.Contains(buildTags(), ",go1.12") {
121+
libc = true
122+
}
123+
trampolines := map[string]bool{}
124+
119125
text := ""
120126
for _, path := range flag.Args() {
121127
file, err := os.Open(path)
@@ -272,6 +278,20 @@ func main() {
272278
sysname = strings.ToUpper(sysname)
273279
}
274280

281+
var libcFn string
282+
if libc {
283+
asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call
284+
sysname = strings.TrimPrefix(sysname, "SYS_") // remove SYS_
285+
sysname = strings.ToLower(sysname) // lowercase
286+
if sysname == "getdirentries64" {
287+
// Special case - libSystem name and
288+
// raw syscall name don't match.
289+
sysname = "__getdirentries64"
290+
}
291+
libcFn = sysname
292+
sysname = "funcPC(libc_" + sysname + "_trampoline)"
293+
}
294+
275295
// Actual call.
276296
arglist := strings.Join(args, ", ")
277297
call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist)
@@ -339,6 +359,16 @@ func main() {
339359
text += "\treturn\n"
340360
text += "}\n\n"
341361

362+
if libc && !trampolines[libcFn] {
363+
// some system calls share a trampoline, like read and readlen.
364+
trampolines[libcFn] = true
365+
// Declare assembly trampoline.
366+
text += fmt.Sprintf("func libc_%s_trampoline()\n", libcFn)
367+
// Assembly trampoline calls the libc_* function, which this magic
368+
// redirects to use the function from libSystem.
369+
text += fmt.Sprintf("//go:linkname libc_%s libc_%s\n", libcFn, libcFn)
370+
text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn)
371+
}
342372
}
343373
if err := s.Err(); err != nil {
344374
fmt.Fprintf(os.Stderr, err.Error())

0 commit comments

Comments
 (0)