Skip to content

Commit

Permalink
runtime: fix darwin 386/amd64 stack switches
Browse files Browse the repository at this point in the history
A few libc_ calls were missing stack switches.

Unfortunately, adding the stack switches revealed a deeper problem.
systemstack() is fundamentally flawed because when you do

    systemstack(func() { ... })

There's no way to mark the anonymous function as nosplit.  At first I
thought it didn't matter, as that function runs on the g0 stack.  But
nosplit is still required, because some syscalls are done when stack
bounds are not set up correctly (e.g. in a signal handler, which runs
on the g0 stack, but g is still pointing at the g stack).  Instead use
asmcgocall and funcPC, so we can be nosplit all the way down.

Mid-stack inlining now pushes darwin over the nosplit limit also.
Leaving that as a TODO.
Update #23168

This might fix the cause of occasional darwin hangs.
Update #25181

Update #17490

Change-Id: If9c3ef052822c7679f5a1dd192443f714483327e
Reviewed-on: https://go-review.googlesource.com/111258
Reviewed-by: Ian Lance Taylor <[email protected]>
Run-TryBot: Ian Lance Taylor <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
randall77 committed May 19, 2018
1 parent e913729 commit e86c267
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 307 deletions.
14 changes: 6 additions & 8 deletions src/cmd/link/internal/ld/dwarf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,8 @@ func TestInlinedRoutineRecords(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
if runtime.GOOS == "solaris" {
t.Skip("skipping on solaris, pending resolution of issue #23168")
if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
}

const prog = `
Expand Down Expand Up @@ -685,7 +685,6 @@ func main() {
}

func abstractOriginSanity(t *testing.T, flags string) {

// Nothing special about net/http here, this is just a convenient
// way to pull in a lot of code.
const prog = `
Expand Down Expand Up @@ -731,7 +730,6 @@ func main() {
// references.
abscount := 0
for i, die := range ex.dies {

// Does it have an abstract origin?
ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
if !originOK {
Expand Down Expand Up @@ -788,8 +786,8 @@ func TestAbstractOriginSanity(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
if runtime.GOOS == "solaris" {
t.Skip("skipping on solaris, pending resolution of issue #23168")
if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
}

abstractOriginSanity(t, OptInl4)
Expand All @@ -801,8 +799,8 @@ func TestAbstractOriginSanityWithLocationLists(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
if runtime.GOOS == "solaris" {
t.Skip("skipping on solaris, pending resolution of issue #23168")
if runtime.GOOS == "solaris" || runtime.GOOS == "darwin" {
t.Skip("skipping on solaris and darwin, pending resolution of issue #23168")
}
if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" {
t.Skip("skipping on not-amd64 not-x86; location lists not supported")
Expand Down
14 changes: 14 additions & 0 deletions src/runtime/asm_386.s
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,8 @@ TEXT ·asmcgocall(SB),NOSPLIT,$0-12
// come in on the m->g0 stack already.
get_tls(CX)
MOVL g(CX), BP
CMPL BP, $0
JEQ nosave // Don't even have a G yet.
MOVL g_m(BP), BP
MOVL m_g0(BP), SI
MOVL g(CX), DI
Expand Down Expand Up @@ -728,6 +730,18 @@ noswitch:

MOVL AX, ret+8(FP)
RET
nosave:
// Now on a scheduling stack (a pthread-created stack).
SUBL $32, SP
ANDL $~15, SP // alignment, perhaps unnecessary
MOVL DX, 4(SP) // save original stack pointer
MOVL BX, 0(SP) // first argument in x86-32 ABI
CALL AX

MOVL 4(SP), CX // restore original stack pointer
MOVL CX, SP
MOVL AX, ret+8(FP)
RET

// cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// +build !linux !amd64
// +build !linux !arm64
// +build !js
// +build !darwin !amd64
// +build !darwin !386

package runtime

Expand Down
11 changes: 5 additions & 6 deletions src/runtime/os_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func newosproc(mp *m) {
// setup and then calls mstart.
var oset sigset
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
_, err = pthread_create(&attr, funcPC(mstart_stub), unsafe.Pointer(mp))
err = pthread_create(&attr, funcPC(mstart_stub), unsafe.Pointer(mp))
sigprocmask(_SIG_SETMASK, &oset, nil)
if err != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
Expand All @@ -175,21 +175,21 @@ func newosproc0(stacksize uintptr, fn uintptr) {
// Initialize an attribute object.
var attr pthreadattr
var err int32
err = pthread_attr_init_trampoline(&attr)
err = pthread_attr_init(&attr)
if err != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}

// Set the stack we want to use.
if pthread_attr_setstacksize_trampoline(&attr, stacksize) != 0 {
if pthread_attr_setstacksize(&attr, stacksize) != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
mSysStatInc(&memstats.stacks_sys, stacksize)

// Tell the pthread library we won't join with this thread.
if pthread_attr_setdetachstate_trampoline(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
exit(1)
}
Expand All @@ -198,8 +198,7 @@ func newosproc0(stacksize uintptr, fn uintptr) {
// setup and then calls mstart.
var oset sigset
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
var t pthread
err = pthread_create_trampoline(&t, &attr, fn, nil)
err = pthread_create(&attr, fn, nil)
sigprocmask(_SIG_SETMASK, &oset, nil)
if err != 0 {
write(2, unsafe.Pointer(&failthreadcreate[0]), int32(len(failthreadcreate)))
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/stubs2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// +build !windows
// +build !nacl
// +build !js
// +build !darwin !amd64
// +build !darwin !386

package runtime

Expand Down
147 changes: 100 additions & 47 deletions src/runtime/sys_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,131 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin,386 darwin,amd64

package runtime

import "unsafe"

// The *_trampoline functions convert from the Go calling convention to the C calling convention
// and then call the underlying libc function. They are defined in sys_darwin_$ARCH.s.

//go:nowritebarrier
func pthread_attr_init(attr *pthreadattr) (errno int32) {
systemstack(func() {
errno = pthread_attr_init_trampoline(attr)
})
return
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_init(attr *pthreadattr) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_attr_init_trampoline)), unsafe.Pointer(&attr))
}
func pthread_attr_init_trampoline()

//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_setstacksize(attr *pthreadattr, size uintptr) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_attr_setstacksize_trampoline)), unsafe.Pointer(&attr))
}
func pthread_attr_setstacksize_trampoline()

//go:noescape
func pthread_attr_init_trampoline(attr *pthreadattr) int32
//go:nosplit
//go:cgo_unsafe_args
func pthread_attr_setdetachstate(attr *pthreadattr, state int) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_attr_setdetachstate_trampoline)), unsafe.Pointer(&attr))
}
func pthread_attr_setdetachstate_trampoline()

//go:nowritebarrier
func pthread_attr_setstacksize(attr *pthreadattr, size uintptr) (errno int32) {
systemstack(func() {
errno = pthread_attr_setstacksize_trampoline(attr, size)
})
return
//go:nosplit
//go:cgo_unsafe_args
func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32 {
return asmcgocall(unsafe.Pointer(funcPC(pthread_create_trampoline)), unsafe.Pointer(&attr))
}
func pthread_create_trampoline()

//go:noescape
func pthread_attr_setstacksize_trampoline(attr *pthreadattr, size uintptr) int32
//go:nosplit
//go:cgo_unsafe_args
func pthread_kill(thread pthread, sig int) (errno int32) {
return asmcgocall(unsafe.Pointer(funcPC(pthread_kill_trampoline)), unsafe.Pointer(&thread))
}
func pthread_kill_trampoline()

//go:nowritebarrier
func pthread_attr_setdetachstate(attr *pthreadattr, state int) (errno int32) {
systemstack(func() {
errno = pthread_attr_setdetachstate_trampoline(attr, state)
})
//go:nosplit
//go:cgo_unsafe_args
func pthread_self() (t pthread) {
asmcgocall(unsafe.Pointer(funcPC(pthread_self_trampoline)), unsafe.Pointer(&t))
return
}
func pthread_self_trampoline()

func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
args := struct {
addr unsafe.Pointer
n uintptr
prot, flags, fd int32
off uint32
ret1 unsafe.Pointer
ret2 int
}{addr, n, prot, flags, fd, off, nil, 0}
asmcgocall(unsafe.Pointer(funcPC(mmap_trampoline)), unsafe.Pointer(&args))
return args.ret1, args.ret2
}
func mmap_trampoline()

//go:noescape
func pthread_attr_setdetachstate_trampoline(attr *pthreadattr, state int) int32
//go:nosplit
//go:cgo_unsafe_args
func munmap(addr unsafe.Pointer, n uintptr) {
asmcgocall(unsafe.Pointer(funcPC(munmap_trampoline)), unsafe.Pointer(&addr))
}
func munmap_trampoline()

//go:nowritebarrier
func pthread_create(attr *pthreadattr, start uintptr, arg unsafe.Pointer) (t pthread, errno int32) {
systemstack(func() {
errno = pthread_create_trampoline(&t, attr, start, arg)
})
return
//go:nosplit
//go:cgo_unsafe_args
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {
asmcgocall(unsafe.Pointer(funcPC(madvise_trampoline)), unsafe.Pointer(&addr))
}
func madvise_trampoline()

//go:noescape
func pthread_create_trampoline(t *pthread, attr *pthreadattr, start uintptr, arg unsafe.Pointer) int32
//go:nosplit
//go:cgo_unsafe_args
func read(fd int32, p unsafe.Pointer, n int32) int32 {
return asmcgocall(unsafe.Pointer(funcPC(read_trampoline)), unsafe.Pointer(&fd))
}
func read_trampoline()

//go:nowritebarrier
func pthread_kill(thread pthread, sig int) (errno int32) {
systemstack(func() {
errno = pthread_kill_trampoline(thread, sig)
})
return
//go:nosplit
//go:cgo_unsafe_args
func closefd(fd int32) int32 {
return asmcgocall(unsafe.Pointer(funcPC(close_trampoline)), unsafe.Pointer(&fd))
}
func close_trampoline()

//go:noescape
func pthread_kill_trampoline(thread pthread, sig int) int32
//go:nosplit
//go:cgo_unsafe_args
func exit(code int32) {
asmcgocall(unsafe.Pointer(funcPC(exit_trampoline)), unsafe.Pointer(&code))
}
func exit_trampoline()

//go:nowritebarrier
func pthread_self() (t pthread) {
systemstack(func() {
t = pthread_self_trampoline()
})
return
//go:nosplit
//go:cgo_unsafe_args
func usleep(usec uint32) {
asmcgocall(unsafe.Pointer(funcPC(usleep_trampoline)), unsafe.Pointer(&usec))
}
func usleep_trampoline()

//go:noescape
func pthread_self_trampoline() pthread
//go:nosplit
//go:cgo_unsafe_args
func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
return asmcgocall(unsafe.Pointer(funcPC(write_trampoline)), unsafe.Pointer(&fd))
}
func write_trampoline()

//go:nosplit
//go:cgo_unsafe_args
func open(name *byte, mode, perm int32) (ret int32) {
return asmcgocall(unsafe.Pointer(funcPC(open_trampoline)), unsafe.Pointer(&name))
}
func open_trampoline()

// Not used on Darwin, but must be defined.
func exitThread(wait *uint32) {
}

// Tell the linker that the libc_* functions are to be found
// in a system library, with the libc_ prefix missing.
Expand Down
Loading

0 comments on commit e86c267

Please sign in to comment.