Skip to content

Commit d047c91

Browse files
committed
cmd/link,runtime: switch openbsd/amd64 to pthreads
This switches openbsd/amd64 to thread creation via pthreads, rather than doing direct system calls. Update #36435 Change-Id: I1105d5c392aa3e4c445d99c8cb80b927712e3529 Reviewed-on: https://go-review.googlesource.com/c/go/+/250180 Trust: Joel Sing <[email protected]> Run-TryBot: Joel Sing <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]>
1 parent 61debff commit d047c91

14 files changed

+418
-162
lines changed

src/cmd/link/internal/ld/data.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
206206
}
207207

208208
// We need to be able to reference dynimport symbols when linking against
209-
// shared libraries, and Solaris, Darwin and AIX need it always
210-
if !target.IsSolaris() && !target.IsDarwin() && !target.IsAIX() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) {
209+
// shared libraries, and AIX, Darwin, OpenBSD and Solaris always need it.
210+
if !target.IsAIX() && !target.IsDarwin() && !target.IsSolaris() && !target.IsOpenbsd() && rs != 0 && rst == sym.SDYNIMPORT && !target.IsDynlinkingGo() && !ldr.AttrSubSymbol(rs) {
211211
if !(target.IsPPC64() && target.IsExternal() && ldr.SymName(rs) == ".TOC.") {
212212
st.err.Errorf(s, "unhandled relocation for %s (type %d (%s) rtype %d (%s))", ldr.SymName(rs), rst, rst, rt, sym.RelocName(target.Arch, rt))
213213
}

src/cmd/link/internal/ld/lib.go

+1
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,7 @@ func (ctxt *Link) hostlink() {
12731273
}
12741274
case objabi.Hopenbsd:
12751275
argv = append(argv, "-Wl,-nopie")
1276+
argv = append(argv, "-pthread")
12761277
case objabi.Hwindows:
12771278
if windowsgui {
12781279
argv = append(argv, "-mwindows")

src/cmd/link/internal/ld/main.go

+8
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,14 @@ func Main(arch *sys.Arch, theArch Arch) {
183183

184184
interpreter = *flagInterpreter
185185

186+
if *flagBuildid == "" && ctxt.Target.IsOpenbsd() {
187+
// TODO(jsing): Remove once direct syscalls are no longer in use.
188+
// OpenBSD 6.7 onwards will not permit direct syscalls from a
189+
// dynamically linked binary unless it identifies the binary
190+
// contains a .note.go.buildid ELF note. See issue #36435.
191+
*flagBuildid = "go-openbsd"
192+
}
193+
186194
// enable benchmarking
187195
var bench *benchmark.Metrics
188196
if len(*benchmarkFlag) != 0 {

src/runtime/asm_amd64.s

+4
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ needtls:
181181
// skip TLS setup on Darwin
182182
JMP ok
183183
#endif
184+
#ifdef GOOS_openbsd
185+
// skip TLS setup on OpenBSD
186+
JMP ok
187+
#endif
184188

185189
LEAQ runtime·m0+m_tls(SB), DI
186190
CALL runtime·settls(SB)

src/runtime/defs_openbsd.go

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ const (
5454
SA_RESTART = C.SA_RESTART
5555
SA_ONSTACK = C.SA_ONSTACK
5656

57+
PTHREAD_CREATE_DETACHED = C.PTHREAD_CREATE_DETACHED
58+
5759
SIGHUP = C.SIGHUP
5860
SIGINT = C.SIGINT
5961
SIGQUIT = C.SIGQUIT
@@ -129,3 +131,10 @@ type Timeval C.struct_timeval
129131
type Itimerval C.struct_itimerval
130132

131133
type KeventT C.struct_kevent
134+
135+
type Pthread C.pthread_t
136+
type PthreadAttr C.pthread_attr_t
137+
type PthreadCond C.pthread_cond_t
138+
type PthreadCondAttr C.pthread_condattr_t
139+
type PthreadMutex C.pthread_mutex_t
140+
type PthreadMutexAttr C.pthread_mutexattr_t

src/runtime/defs_openbsd_amd64.go

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const (
3030
_SA_RESTART = 0x2
3131
_SA_ONSTACK = 0x1
3232

33+
_PTHREAD_CREATE_DETACHED = 0x1
34+
3335
_SIGHUP = 0x1
3436
_SIGINT = 0x2
3537
_SIGQUIT = 0x3
@@ -177,3 +179,10 @@ type keventt struct {
177179
data int64
178180
udata *byte
179181
}
182+
183+
type pthread uintptr
184+
type pthreadattr uintptr
185+
type pthreadcond uintptr
186+
type pthreadcondattr uintptr
187+
type pthreadmutex uintptr
188+
type pthreadmutexattr uintptr

src/runtime/os_openbsd.go

-34
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runtime
66

77
import (
88
"runtime/internal/atomic"
9-
"runtime/internal/sys"
109
"unsafe"
1110
)
1211

@@ -47,9 +46,6 @@ func raiseproc(sig uint32)
4746
func getthrid() int32
4847
func thrkill(tid int32, sig int)
4948

50-
//go:noescape
51-
func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
52-
5349
//go:noescape
5450
func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32
5551

@@ -183,36 +179,6 @@ func semawakeup(mp *m) {
183179
}
184180
}
185181

186-
// May run with m.p==nil, so write barriers are not allowed.
187-
//go:nowritebarrier
188-
func newosproc(mp *m) {
189-
stk := unsafe.Pointer(mp.g0.stack.hi)
190-
if false {
191-
print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
192-
}
193-
194-
// Stack pointer must point inside stack area (as marked with MAP_STACK),
195-
// rather than at the top of it.
196-
param := tforkt{
197-
tf_tcb: unsafe.Pointer(&mp.tls[0]),
198-
tf_tid: nil, // minit will record tid
199-
tf_stack: uintptr(stk) - sys.PtrSize,
200-
}
201-
202-
var oset sigset
203-
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
204-
ret := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart))
205-
sigprocmask(_SIG_SETMASK, &oset, nil)
206-
207-
if ret < 0 {
208-
print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
209-
if ret == -_EAGAIN {
210-
println("runtime: may need to increase max user processes (ulimit -p)")
211-
}
212-
throw("runtime.newosproc")
213-
}
214-
}
215-
216182
func osinit() {
217183
ncpu = getncpu()
218184
physPageSize = getPageSize()

src/runtime/os_openbsd_libc.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2020 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 openbsd,amd64
6+
7+
package runtime
8+
9+
import (
10+
"unsafe"
11+
)
12+
13+
var failThreadCreate = []byte("runtime: failed to create new OS thread\n")
14+
15+
// mstart_stub provides glue code to call mstart from pthread_create.
16+
func mstart_stub()
17+
18+
// May run with m.p==nil, so write barriers are not allowed.
19+
//go:nowritebarrierrec
20+
func newosproc(mp *m) {
21+
if false {
22+
print("newosproc m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
23+
}
24+
25+
// Initialize an attribute object.
26+
var attr pthreadattr
27+
if err := pthread_attr_init(&attr); err != 0 {
28+
write(2, unsafe.Pointer(&failThreadCreate[0]), int32(len(failThreadCreate)))
29+
exit(1)
30+
}
31+
32+
// Find out OS stack size for our own stack guard.
33+
var stacksize uintptr
34+
if pthread_attr_getstacksize(&attr, &stacksize) != 0 {
35+
write(2, unsafe.Pointer(&failThreadCreate[0]), int32(len(failThreadCreate)))
36+
exit(1)
37+
}
38+
mp.g0.stack.hi = stacksize // for mstart
39+
40+
// Tell the pthread library we won't join with this thread.
41+
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
42+
write(2, unsafe.Pointer(&failThreadCreate[0]), int32(len(failThreadCreate)))
43+
exit(1)
44+
}
45+
46+
// Finally, create the thread. It starts at mstart_stub, which does some low-level
47+
// setup and then calls mstart.
48+
var oset sigset
49+
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
50+
err := pthread_create(&attr, funcPC(mstart_stub), unsafe.Pointer(mp))
51+
sigprocmask(_SIG_SETMASK, &oset, nil)
52+
if err != 0 {
53+
write(2, unsafe.Pointer(&failThreadCreate[0]), int32(len(failThreadCreate)))
54+
exit(1)
55+
}
56+
57+
pthread_attr_destroy(&attr)
58+
}

src/runtime/os_openbsd_syscall.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2011 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 openbsd,!amd64
6+
7+
package runtime
8+
9+
import (
10+
"runtime/internal/sys"
11+
"unsafe"
12+
)
13+
14+
//go:noescape
15+
func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
16+
17+
// May run with m.p==nil, so write barriers are not allowed.
18+
//go:nowritebarrier
19+
func newosproc(mp *m) {
20+
stk := unsafe.Pointer(mp.g0.stack.hi)
21+
if false {
22+
print("newosproc stk=", stk, " m=", mp, " g=", mp.g0, " id=", mp.id, " ostk=", &mp, "\n")
23+
}
24+
25+
// Stack pointer must point inside stack area (as marked with MAP_STACK),
26+
// rather than at the top of it.
27+
param := tforkt{
28+
tf_tcb: unsafe.Pointer(&mp.tls[0]),
29+
tf_tid: nil, // minit will record tid
30+
tf_stack: uintptr(stk) - sys.PtrSize,
31+
}
32+
33+
var oset sigset
34+
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
35+
ret := tfork(&param, unsafe.Sizeof(param), mp, mp.g0, funcPC(mstart))
36+
sigprocmask(_SIG_SETMASK, &oset, nil)
37+
38+
if ret < 0 {
39+
print("runtime: failed to create new OS thread (have ", mcount()-1, " already; errno=", -ret, ")\n")
40+
if ret == -_EAGAIN {
41+
println("runtime: may need to increase max user processes (ulimit -p)")
42+
}
43+
throw("runtime.newosproc")
44+
}
45+
}

src/runtime/proc.go

+5
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,11 @@ func mStackIsSystemAllocated() bool {
12221222
switch GOOS {
12231223
case "aix", "darwin", "plan9", "illumos", "ios", "solaris", "windows":
12241224
return true
1225+
case "openbsd":
1226+
switch GOARCH {
1227+
case "amd64":
1228+
return true
1229+
}
12251230
}
12261231
return false
12271232
}

src/runtime/sys_darwin.go

-44
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,6 @@ package runtime
66

77
import "unsafe"
88

9-
// Call fn with arg as its argument. Return what fn returns.
10-
// fn is the raw pc value of the entry point of the desired function.
11-
// Switches to the system stack, if not already there.
12-
// Preserves the calling point as the location where a profiler traceback will begin.
13-
//go:nosplit
14-
func libcCall(fn, arg unsafe.Pointer) int32 {
15-
// Leave caller's PC/SP/G around for traceback.
16-
gp := getg()
17-
var mp *m
18-
if gp != nil {
19-
mp = gp.m
20-
}
21-
if mp != nil && mp.libcallsp == 0 {
22-
mp.libcallg.set(gp)
23-
mp.libcallpc = getcallerpc()
24-
// sp must be the last, because once async cpu profiler finds
25-
// all three values to be non-zero, it will use them
26-
mp.libcallsp = getcallersp()
27-
} else {
28-
// Make sure we don't reset libcallsp. This makes
29-
// libcCall reentrant; We remember the g/pc/sp for the
30-
// first call on an M, until that libcCall instance
31-
// returns. Reentrance only matters for signals, as
32-
// libc never calls back into Go. The tricky case is
33-
// where we call libcX from an M and record g/pc/sp.
34-
// Before that call returns, a signal arrives on the
35-
// same M and the signal handling code calls another
36-
// libc function. We don't want that second libcCall
37-
// from within the handler to be recorded, and we
38-
// don't want that call's completion to zero
39-
// libcallsp.
40-
// We don't need to set libcall* while we're in a sighandler
41-
// (even if we're not currently in libc) because we block all
42-
// signals while we're handling a signal. That includes the
43-
// profile signal, which is the one that uses the libcall* info.
44-
mp = nil
45-
}
46-
res := asmcgocall(fn, arg)
47-
if mp != nil {
48-
mp.libcallsp = 0
49-
}
50-
return res
51-
}
52-
539
// The X versions of syscall expect the libc call to return a 64-bit result.
5410
// Otherwise (the non-X version) expects a 32-bit result.
5511
// This distinction is required because an error is indicated by returning -1,

src/runtime/sys_libc.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 openbsd,amd64
6+
7+
package runtime
8+
9+
import "unsafe"
10+
11+
// Call fn with arg as its argument. Return what fn returns.
12+
// fn is the raw pc value of the entry point of the desired function.
13+
// Switches to the system stack, if not already there.
14+
// Preserves the calling point as the location where a profiler traceback will begin.
15+
//go:nosplit
16+
func libcCall(fn, arg unsafe.Pointer) int32 {
17+
// Leave caller's PC/SP/G around for traceback.
18+
gp := getg()
19+
var mp *m
20+
if gp != nil {
21+
mp = gp.m
22+
}
23+
if mp != nil && mp.libcallsp == 0 {
24+
mp.libcallg.set(gp)
25+
mp.libcallpc = getcallerpc()
26+
// sp must be the last, because once async cpu profiler finds
27+
// all three values to be non-zero, it will use them
28+
mp.libcallsp = getcallersp()
29+
} else {
30+
// Make sure we don't reset libcallsp. This makes
31+
// libcCall reentrant; We remember the g/pc/sp for the
32+
// first call on an M, until that libcCall instance
33+
// returns. Reentrance only matters for signals, as
34+
// libc never calls back into Go. The tricky case is
35+
// where we call libcX from an M and record g/pc/sp.
36+
// Before that call returns, a signal arrives on the
37+
// same M and the signal handling code calls another
38+
// libc function. We don't want that second libcCall
39+
// from within the handler to be recorded, and we
40+
// don't want that call's completion to zero
41+
// libcallsp.
42+
// We don't need to set libcall* while we're in a sighandler
43+
// (even if we're not currently in libc) because we block all
44+
// signals while we're handling a signal. That includes the
45+
// profile signal, which is the one that uses the libcall* info.
46+
mp = nil
47+
}
48+
res := asmcgocall(fn, arg)
49+
if mp != nil {
50+
mp.libcallsp = 0
51+
}
52+
return res
53+
}

0 commit comments

Comments
 (0)