diff mbox series

Update: Hurd port for gcc-7 go : 7.3.0-8+ for glibc 2.26+

Message ID 1520706815.3197.22.camel@gmail.com
State New
Headers show
Series Update: Hurd port for gcc-7 go : 7.3.0-8+ for glibc 2.26+ | expand

Commit Message

Svante Signell March 10, 2018, 6:33 p.m. UTC
Hi,

Attached is the updated patch, src_libgo_build.diff, to build gccgo properly on
Debian GNU/Hurd on gcc-7 (7-7.3.0-{8,9,10}) again after the update of glibc to
2.26+

The libgo tests show the following:

		=== libgo Summary ===

# of expected passes		119
# of unexpected failures	26

Replacing the stub code of netpoll_gnu.go with a poll-based implementation the
libgo tests improves to:

                === libgo Summary ===

# of expected passes            124
# of unexpected failures        21

The change is reflected in the updated version of src_libgo_go_runtime.diff,
also attached!

Thanks!

Comments

Samuel Thibault March 10, 2018, 11:58 p.m. UTC | #1
Hello,

Svante Signell, on sam. 10 mars 2018 19:33:35 +0100, wrote:
> Attached is the updated patch, src_libgo_build.diff, to build gccgo properly on
> Debian GNU/Hurd on gcc-7 (7-7.3.0-{8,9,10}) again after the update of glibc to
> 2.26+

I have updated the gcc-7 package in Debian, thanks!

Samuel
diff mbox series

Patch

Index: gcc-7-7.3.0-8.1/src/libgo/go/runtime/netpoll_gnu.go
===================================================================
--- /dev/null
+++ gcc-7-7.3.0-8.1/src/libgo/go/runtime/netpoll_gnu.go
@@ -0,0 +1,303 @@ 
+// Copyright 2013 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.
+
+// FIXME: Fake network poller for gnu.
+// This is based on the former libgo/runtime/netpoll_select.c implementation
+// except that it uses poll instead of select and is written in Go.
+// Inspiration was also taken from netpoll_aix.go and netpoll_solaris.go
+
+// +build gnu
+
+package runtime
+
+import (
+	"unsafe"
+)
+
+//From /usr/include/i386-gnu/bits/poll.h
+const _POLLIN   =  01           // There is data to read.
+const _POLLPRI  =  02           // There is urgent data to read.
+const _POLLOUT  =  04           // Writing now will not block.
+const _POLLERR  = 010           // Error condition.
+const _POLLHUP  = 020           // Hung up.
+const _POLLNVAL = 040           // Invalid polling request.
+
+type pollfd struct {
+        fd      int32           // File descriptor to poll.
+        events  int16           // Types of events poller cares about.
+        revents int16           // Types of events that actually occurred.
+}
+
+//From /usr/include/x86_64-linux-gnu/sys/poll.h
+//extern poll
+func poll (polldata_array *pollfd, nfds int32, timeout int32) int32
+
+//extern pipe2
+func libc_pipe2(fd *int32, flags int32) int32
+
+var (
+	pipefd      [2]int32
+	pollfds     int32 = -1
+	nfds        int32 = 0
+	rdwake      int32
+	wrwake      int32
+	fds         [207]pollfd
+	data        [207]*pollDesc
+	pd          *pollDesc
+	pmtx        mutex
+	needsUpdate bool
+	b           byte
+	err         int32 = 0
+)
+
+func netpollinit() {
+
+	ret := libc_pipe2(&pipefd[0], _O_CLOEXEC|_O_NONBLOCK);
+	if ret == -1 {
+		throw("runtime:netpollinit(): failed to create pipe2")
+	}
+
+	rdwake = pipefd[0]
+	wrwake = pipefd[1]
+
+	// Add the read side of the pipe to the pollset.
+	lock(&pmtx)
+	fds[0].fd = int32(rdwake)
+	fds[0].events = int16(_POLLIN)
+	fds[0].revents = int16(0)
+
+// 	Checks for pd != nil are made in netpoll()
+	data[0] = nil
+	unlock(&pmtx)
+
+	nfds = 1
+	pollfds = 1
+
+	return
+}
+
+func netpolldescriptor() uintptr {
+	// FIXME: see src/libgo/go/os/exec/exec_test.go
+	// Need to return two fds here: wrwake and rdwake
+	return ^uintptr(0)
+}
+
+func fdadd(fd uintptr, events int16, pd *pollDesc) {
+//	println("netpollopen:fdadd: nfds =", nfds, "fd =", fd)
+
+	fdfound := false
+	// Omit fds[0].fd = rdwake
+	for i := int32(1); i < nfds; i++ {
+		fdsi := fds[i]
+		if fdsi.fd == int32(fd) {
+			fdfound = true
+			fdsi.events = int16(events)
+			fdsi.revents = int16(0)
+			data[i] = pd
+			break
+		}
+	}
+	// fd not found, add it.
+	if fdfound == false {
+		fds[nfds].fd = int32(fd)
+		fds[nfds].events = int16(events)
+		fds[nfds].revents = int16(0)
+		data[nfds] = pd
+		nfds++
+	}
+
+//	for l := int32(0); l < nfds; l++ {
+//		println("fds[", l, "].fd =", fds[l].fd)
+//	}
+
+	return
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) int32 {
+	lock(&pmtx)
+	needsUpdate = true
+	unlock(&pmtx)
+
+	// poll will block so wakeup using wrwake first.
+wrloop1:
+	nwritten := write(uintptr(wrwake), unsafe.Pointer(&b), 1)
+	if nwritten == 0 {
+		println("runtime:netpollopen: write retuned zero, fd =", wrwake)
+		return -1
+	}
+	if nwritten == -1 {
+		err = int32(errno())
+		if err == _EAGAIN {
+			goto wrloop1
+		}
+		println("runtime:netpollopen: write failed fd =", wrwake, "errno =", err)
+		return err
+	}
+
+	// Add fd to the pollset.
+	lock(&pmtx)
+	fdadd(fd, _POLLIN|_POLLOUT, pd)
+	needsUpdate = false
+	unlock(&pmtx)
+
+	return 0
+}
+
+func fdremove(fd uintptr) {
+//	println("netpollclose():fdremove() nfds =", nfds, "fd =", fd)
+
+	fdfound := false
+	// Omit fds[0].fd = rdwake
+	j := 1
+	for i := int32(1); i < nfds; i++ {
+		fdsi := fds[i]
+		if fdsi.fd == int32(fd) {
+			fdfound = true
+			fdsi.fd = int32(0)
+			fdsi.events = int16(0)
+			fdsi.revents = int16(0)
+			data[i] = nil
+		} else {
+			fds[j].fd = fdsi.fd
+			fds[j].events = fdsi.events
+			fds[j].revents = int16(0)
+			data[j] = data[i]
+			j++
+		}
+	}
+	nfds--
+	// fd not found, print an error
+	//FIXME: Still output from here
+	if fdfound == false {
+		println("netpollclose:fdremove: fd =", fd, "NOT FOUND")
+	}
+
+//	for l := int32(0); l < nfds; l++ {
+//		println("fds[", l, "].fd =", fds[l].fd)
+//	}
+
+	return
+}
+
+func netpollclose(fd uintptr) int32 {
+	lock(&pmtx)
+	needsUpdate = true
+	unlock(&pmtx)
+
+	// poll will block so wakeup using wrwake first.
+wrloop2:
+	nwritten := write(uintptr(wrwake), unsafe.Pointer(&b), 1)
+	if nwritten == 0 {
+		println("runtime:netpollclose: write retuned zero, fd =", wrwake)
+		return -1
+	}
+	if nwritten == -1 {
+		err = int32(errno())
+		if err == _EAGAIN {
+			goto wrloop2
+		}
+		println("runtime:netpollclose: write failed fd =", wrwake, "errno =", err)
+		return err
+	}
+
+	// Remove fd from the pollset.
+	lock(&pmtx)
+	fdremove(fd)
+	needsUpdate = false
+	unlock(&pmtx)
+
+	return 0
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+	throw("runtime:netpollarm() unused")
+}
+
+// polls for ready network connections
+// returns list of goroutines that become runnable
+func netpoll(block bool) *g {
+	if pollfds == -1 {
+		return nil
+	}
+
+	timeout := int32(-1)
+	if !block {
+		timeout = 0
+	}
+
+retry:
+	lock(&pmtx)
+	if needsUpdate {
+		unlock(&pmtx)
+		osyield()
+		goto retry
+	}
+	unlock(&pmtx)
+
+	// Note: poll only returns fds with non-zero revents!
+	nfound := poll(&fds[0], nfds, timeout)
+	if nfound == 0 {
+		return nil
+	}
+	if nfound < 0 {
+		err = int32(errno())
+		if err == _EINTR || err == _EAGAIN {
+			goto retry
+		}
+		println("runtime: poll failed errno =", err)
+		throw("runtime: netpoll failed")
+	}
+
+	var mode int32
+	var gp guintptr
+	// We assume that nfound <= nfds
+	for i := int32(0); i < nfds; i++ {
+		fdsi := &fds[i]
+
+		// Skip fds with zero revents as poll does
+		if fdsi.revents == 0 {
+			continue
+		}
+
+	    	mode = 0
+		if fdsi.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
+			if fdsi.fd == rdwake {
+rdloop:
+				nread := read(fdsi.fd, unsafe.Pointer(&b), 1)
+				// EOF
+				if nread == 0 {
+					println("runtime:netpoll: read returned zero fd =", fdsi.fd)
+					return nil
+				}
+				if nread == -1 {
+					err = int32(errno())
+					if err == _EAGAIN {
+						goto rdloop
+					}
+				 	println("runtime:netpoll: read failed fd =", fdsi.fd, "errno =", err)
+				 	return nil
+				}
+				continue
+			}
+			mode += 'r'
+		}
+		if fdsi.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
+			mode += 'w'
+		}
+		if mode != 0 {
+			lock(&pmtx)
+			pd = data[i]
+			unlock(&pmtx)
+			if pd != nil {
+				netpollready(&gp, pd, mode)
+			}
+		}
+	}
+
+	if block && gp == 0 {
+		goto retry
+	}
+
+        return gp.ptr()
+}
Index: gcc-7-7.3.0-8.1/src/libgo/go/runtime/os_gnu.go
===================================================================
--- /dev/null
+++ gcc-7-7.3.0-8.1/src/libgo/go/runtime/os_gnu.go
@@ -0,0 +1,86 @@ 
+// Copyright 2011 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.
+
+package runtime
+
+import "unsafe"
+
+type mOS struct {
+	waitsema uintptr // semaphore for parking on locks
+}
+
+//extern malloc
+func libc_malloc(uintptr) unsafe.Pointer
+
+//go:noescape
+//extern sem_init
+func sem_init(sem *_sem_t, pshared int32, value uint32) int32
+
+//go:noescape
+//extern sem_wait
+func sem_wait(sem *_sem_t) int32
+
+//go:noescape
+//extern sem_post
+func sem_post(sem *_sem_t) int32
+
+//go:noescape
+//extern sem_timedwait
+func sem_timedwait(sem *_sem_t, timeout *timespec) int32
+
+//go:nosplit
+func semacreate(mp *m) {
+	if mp.mos.waitsema != 0 {
+		return
+	}
+
+	var sem *_sem_t
+
+	// Call libc's malloc rather than malloc. This will
+	// allocate space on the C heap. We can't call malloc
+	// here because it could cause a deadlock.
+	sem = (*_sem_t)(libc_malloc(unsafe.Sizeof(*sem)))
+	if sem_init(sem, 0, 0) != 0 {
+		throw("sem_init")
+	}
+	mp.mos.waitsema = uintptr(unsafe.Pointer(sem))
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+	_m_ := getg().m
+	if ns >= 0 {
+		var ts timespec
+		ts.set_sec(ns / 1000000000)
+		ts.set_nsec(int32(ns % 1000000000))
+
+		if sem_timedwait((*_sem_t)(unsafe.Pointer(_m_.mos.waitsema)), &ts) != 0 {
+			err := errno()
+			if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
+				return -1
+			}
+			throw("sem_timedwait")
+		}
+		return 0
+	}
+	for {
+		r1 := sem_wait((*_sem_t)(unsafe.Pointer(_m_.mos.waitsema)))
+		if r1 == 0 {
+			break
+		}
+		if errno() == _EINTR {
+			continue
+		}
+		throw("sem_wait")
+	}
+	return 0
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+	if sem_post((*_sem_t)(unsafe.Pointer(mp.mos.waitsema))) != 0 {
+		throw("sem_post")
+	}
+}
+
Index: gcc-7-7.3.0-8.1/src/libgo/go/runtime/signal_gnu.go
===================================================================
--- /dev/null
+++ gcc-7-7.3.0-8.1/src/libgo/go/runtime/signal_gnu.go
@@ -0,0 +1,606 @@ 
+// Copyright 2012 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.
+
+// +build gnu
+
+package runtime
+
+import (
+	"runtime/internal/sys"
+	"unsafe"
+)
+
+// For gccgo's C code to call:
+//go:linkname initsig runtime.initsig
+//go:linkname crash runtime.crash
+//go:linkname resetcpuprofiler runtime.resetcpuprofiler
+//go:linkname sigtrampgo runtime.sigtrampgo
+
+//go:linkname os_sigpipe os.sigpipe
+func os_sigpipe() {
+	systemstack(sigpipe)
+}
+
+func signame(sig uint32) string {
+	if sig >= uint32(len(sigtable)) {
+		return ""
+	}
+	return sigtable[sig].name
+}
+
+const (
+	_SIG_DFL uintptr = 0
+	_SIG_IGN uintptr = 1
+)
+
+// Stores the signal handlers registered before Go installed its own.
+// These signal handlers will be invoked in cases where Go doesn't want to
+// handle a particular signal (e.g., signal occurred on a non-Go thread).
+// See sigfwdgo() for more information on when the signals are forwarded.
+//
+// Signal forwarding is currently available only on Darwin and Linux.
+var fwdSig [_NSIG]uintptr
+
+// channels for synchronizing signal mask updates with the signal mask
+// thread
+var (
+	disableSigChan  chan uint32
+	enableSigChan   chan uint32
+	maskUpdatedChan chan struct{}
+)
+
+func init() {
+	// _NSIG is the number of signals on this operating system.
+	// sigtable should describe what to do for all the possible signals.
+	if len(sigtable) != _NSIG {
+		print("runtime: len(sigtable)=", len(sigtable), " _NSIG=", _NSIG, "\n")
+		throw("bad sigtable len")
+	}
+}
+
+var signalsOK bool
+
+// Initialize signals.
+// Called by libpreinit so runtime may not be initialized.
+//go:nosplit
+//go:nowritebarrierrec
+func initsig(preinit bool) {
+	if preinit {
+		// preinit is only passed as true if isarchive should be true.
+		isarchive = true
+	}
+
+	if !preinit {
+		// It's now OK for signal handlers to run.
+		signalsOK = true
+	}
+
+	// For c-archive/c-shared this is called by libpreinit with
+	// preinit == true.
+	if (isarchive || islibrary) && !preinit {
+		return
+	}
+
+	for i := uint32(0); i < _NSIG; i++ {
+		t := &sigtable[i]
+		if t.flags == 0 || t.flags&_SigDefault != 0 {
+			continue
+		}
+		fwdSig[i] = getsig(i)
+
+		if !sigInstallGoHandler(i) {
+			// Even if we are not installing a signal handler,
+			// set SA_ONSTACK if necessary.
+			if fwdSig[i] != _SIG_DFL && fwdSig[i] != _SIG_IGN {
+				setsigstack(i)
+			}
+			continue
+		}
+
+		t.flags |= _SigHandling
+		setsig(i, getSigtramp())
+	}
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func sigInstallGoHandler(sig uint32) bool {
+	// For some signals, we respect an inherited SIG_IGN handler
+	// rather than insist on installing our own default handler.
+	// Even these signals can be fetched using the os/signal package.
+	switch sig {
+	case _SIGHUP, _SIGINT:
+		if fwdSig[sig] == _SIG_IGN {
+			return false
+		}
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigSetStack != 0 {
+		return false
+	}
+
+	// When built using c-archive or c-shared, only install signal
+	// handlers for synchronous signals.
+	if (isarchive || islibrary) && t.flags&_SigPanic == 0 {
+		return false
+	}
+
+	return true
+}
+
+func sigenable(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		enableSigChan <- sig
+		<-maskUpdatedChan
+		if t.flags&_SigHandling == 0 {
+			t.flags |= _SigHandling
+			fwdSig[sig] = getsig(sig)
+			setsig(sig, getSigtramp())
+		}
+	}
+}
+
+func sigdisable(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		disableSigChan <- sig
+		<-maskUpdatedChan
+
+		// If initsig does not install a signal handler for a
+		// signal, then to go back to the state before Notify
+		// we should remove the one we installed.
+		if !sigInstallGoHandler(sig) {
+			t.flags &^= _SigHandling
+			setsig(sig, fwdSig[sig])
+		}
+	}
+}
+
+func sigignore(sig uint32) {
+	if sig >= uint32(len(sigtable)) {
+		return
+	}
+
+	t := &sigtable[sig]
+	if t.flags&_SigNotify != 0 {
+		t.flags &^= _SigHandling
+		setsig(sig, _SIG_IGN)
+	}
+}
+
+func resetcpuprofiler(hz int32) {
+	var it _itimerval
+	if hz == 0 {
+		setitimer(_ITIMER_PROF, &it, nil)
+	} else {
+		it.it_interval.tv_sec = 0
+		it.it_interval.set_usec(1000000 / hz)
+		it.it_value = it.it_interval
+		setitimer(_ITIMER_PROF, &it, nil)
+	}
+	_g_ := getg()
+	_g_.m.profilehz = hz
+}
+
+func sigpipe() {
+	if sigsend(_SIGPIPE) {
+		return
+	}
+	dieFromSignal(_SIGPIPE)
+}
+
+// sigtrampgo is called from the signal handler function, sigtramp,
+// written in assembly code.
+// This is called by the signal handler, and the world may be stopped.
+//go:nosplit
+//go:nowritebarrierrec
+func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) {
+	if sigfwdgo(sig, info, ctx) {
+		return
+	}
+	g := getg()
+	if g == nil {
+		c := sigctxt{info, ctx}
+		if sig == _SIGPROF {
+			_, pc := getSiginfo(info, ctx)
+			sigprofNonGoPC(pc)
+			return
+		}
+		badsignal(uintptr(sig), &c)
+		return
+	}
+
+	setg(g.m.gsignal)
+	sighandler(sig, info, ctx, g)
+	setg(g)
+}
+
+// sigpanic turns a synchronous signal into a run-time panic.
+// If the signal handler sees a synchronous panic, it arranges the
+// stack to look like the function where the signal occurred called
+// sigpanic, sets the signal's PC value to sigpanic, and returns from
+// the signal handler. The effect is that the program will act as
+// though the function that got the signal simply called sigpanic
+// instead.
+func sigpanic() {
+	g := getg()
+	if !canpanic(g) {
+		throw("unexpected signal during runtime execution")
+	}
+
+	switch g.sig {
+	case _SIGBUS:
+		if g.sigcode0 == _BUS_ADRERR && g.sigcode1 < 0x1000 {
+			panicmem()
+		}
+		// Support runtime/debug.SetPanicOnFault.
+		if g.paniconfault {
+			panicmem()
+		}
+		print("unexpected fault address ", hex(g.sigcode1), "\n")
+		throw("fault")
+	case _SIGSEGV:
+		if (g.sigcode0 == 0 || g.sigcode0 == _SEGV_MAPERR || g.sigcode0 == _SEGV_ACCERR) && g.sigcode1 < 0x1000 {
+			panicmem()
+		}
+		// Support runtime/debug.SetPanicOnFault.
+		if g.paniconfault {
+			panicmem()
+		}
+		print("unexpected fault address ", hex(g.sigcode1), "\n")
+		throw("fault")
+	case _SIGFPE:
+		switch g.sigcode0 {
+		case _FPE_INTDIV:
+			panicdivide()
+		case _FPE_INTOVF:
+			panicoverflow()
+		}
+		panicfloat()
+	}
+
+	if g.sig >= uint32(len(sigtable)) {
+		// can't happen: we looked up g.sig in sigtable to decide to call sigpanic
+		throw("unexpected signal value")
+	}
+	panic(errorString(sigtable[g.sig].name))
+}
+
+// dieFromSignal kills the program with a signal.
+// This provides the expected exit status for the shell.
+// This is only called with fatal signals expected to kill the process.
+//go:nosplit
+//go:nowritebarrierrec
+func dieFromSignal(sig uint32) {
+	setsig(sig, _SIG_DFL)
+	unblocksig(sig)
+	raise(sig)
+
+	// That should have killed us. On some systems, though, raise
+	// sends the signal to the whole process rather than to just
+	// the current thread, which means that the signal may not yet
+	// have been delivered. Give other threads a chance to run and
+	// pick up the signal.
+	osyield()
+	osyield()
+	osyield()
+
+	// If we are still somehow running, just exit with the wrong status.
+	exit(2)
+}
+
+// raisebadsignal is called when a signal is received on a non-Go
+// thread, and the Go program does not want to handle it (that is, the
+// program has not called os/signal.Notify for the signal).
+func raisebadsignal(sig uint32, c *sigctxt) {
+	if sig == _SIGPROF {
+		// Ignore profiling signals that arrive on non-Go threads.
+		return
+	}
+
+	var handler uintptr
+	if sig >= _NSIG {
+		handler = _SIG_DFL
+	} else {
+		handler = fwdSig[sig]
+	}
+
+	// Reset the signal handler and raise the signal.
+	// We are currently running inside a signal handler, so the
+	// signal is blocked. We need to unblock it before raising the
+	// signal, or the signal we raise will be ignored until we return
+	// from the signal handler. We know that the signal was unblocked
+	// before entering the handler, or else we would not have received
+	// it. That means that we don't have to worry about blocking it
+	// again.
+	unblocksig(sig)
+	setsig(sig, handler)
+
+	// If we're linked into a non-Go program we want to try to
+	// avoid modifying the original context in which the signal
+	// was raised. If the handler is the default, we know it
+	// is non-recoverable, so we don't have to worry about
+	// re-installing sighandler. At this point we can just
+	// return and the signal will be re-raised and caught by
+	// the default handler with the correct context.
+	if (isarchive || islibrary) && handler == _SIG_DFL && c.sigcode() != _SI_USER {
+		return
+	}
+
+	raise(sig)
+
+	// Give the signal a chance to be delivered.
+	// In almost all real cases the program is about to crash,
+	// so sleeping here is not a waste of time.
+	usleep(1000)
+
+	// If the signal didn't cause the program to exit, restore the
+	// Go signal handler and carry on.
+	//
+	// We may receive another instance of the signal before we
+	// restore the Go handler, but that is not so bad: we know
+	// that the Go program has been ignoring the signal.
+	setsig(sig, getSigtramp())
+}
+
+func crash() {
+	if GOOS == "darwin" {
+		// OS X core dumps are linear dumps of the mapped memory,
+		// from the first virtual byte to the last, with zeros in the gaps.
+		// Because of the way we arrange the address space on 64-bit systems,
+		// this means the OS X core file will be >128 GB and even on a zippy
+		// workstation can take OS X well over an hour to write (uninterruptible).
+		// Save users from making that mistake.
+		if sys.PtrSize == 8 {
+			return
+		}
+	}
+
+	dieFromSignal(_SIGIOT)
+}
+
+// ensureSigM starts one global, sleeping thread to make sure at least one thread
+// is available to catch signals enabled for os/signal.
+func ensureSigM() {
+	if maskUpdatedChan != nil {
+		return
+	}
+	maskUpdatedChan = make(chan struct{})
+	disableSigChan = make(chan uint32)
+	enableSigChan = make(chan uint32)
+	go func() {
+		// Signal masks are per-thread, so make sure this goroutine stays on one
+		// thread.
+		LockOSThread()
+		defer UnlockOSThread()
+		// The sigBlocked mask contains the signals not active for os/signal,
+		// initially all signals except the essential. When signal.Notify()/Stop is called,
+		// sigenable/sigdisable in turn notify this thread to update its signal
+		// mask accordingly.
+		var sigBlocked sigset
+		sigfillset(&sigBlocked)
+		for i := range sigtable {
+			if sigtable[i].flags&_SigUnblock != 0 {
+				sigdelset(&sigBlocked, i)
+			}
+		}
+		sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
+		for {
+			select {
+			case sig := <-enableSigChan:
+				if sig > 0 {
+					sigdelset(&sigBlocked, int(sig))
+				}
+			case sig := <-disableSigChan:
+				if sig > 0 {
+					sigaddset(&sigBlocked, int(sig))
+				}
+			}
+			sigprocmask(_SIG_SETMASK, &sigBlocked, nil)
+			maskUpdatedChan <- struct{}{}
+		}
+	}()
+}
+
+// This is called when we receive a signal when there is no signal stack.
+// This can only happen if non-Go code calls sigaltstack to disable the
+// signal stack.
+func noSignalStack(sig uint32) {
+	println("signal", sig, "received on thread with no signal stack")
+	throw("non-Go code disabled sigaltstack")
+}
+
+// This is called if we receive a signal when there is a signal stack
+// but we are not on it. This can only happen if non-Go code called
+// sigaction without setting the SS_ONSTACK flag.
+func sigNotOnStack(sig uint32) {
+	println("signal", sig, "received but handler not on signal stack")
+	throw("non-Go code set up signal handler without SA_ONSTACK flag")
+}
+
+// This runs on a foreign stack, without an m or a g. No stack split.
+//go:nosplit
+//go:norace
+//go:nowritebarrierrec
+func badsignal(sig uintptr, c *sigctxt) {
+	needm(0)
+	if !sigsend(uint32(sig)) {
+		// A foreign thread received the signal sig, and the
+		// Go code does not want to handle it.
+		raisebadsignal(uint32(sig), c)
+	}
+	dropm()
+}
+
+// Determines if the signal should be handled by Go and if not, forwards the
+// signal to the handler that was installed before Go's. Returns whether the
+// signal was forwarded.
+// This is called by the signal handler, and the world may be stopped.
+//go:nosplit
+//go:nowritebarrierrec
+func sigfwdgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) bool {
+	if sig >= uint32(len(sigtable)) {
+		return false
+	}
+	fwdFn := fwdSig[sig]
+
+	if !signalsOK {
+		// The only way we can get here is if we are in a
+		// library or archive, we installed a signal handler
+		// at program startup, but the Go runtime has not yet
+		// been initialized.
+		if fwdFn == _SIG_DFL {
+			dieFromSignal(sig)
+		} else {
+			sigfwd(fwdFn, sig, info, ctx)
+		}
+		return true
+	}
+
+	flags := sigtable[sig].flags
+
+	// If there is no handler to forward to, no need to forward.
+	if fwdFn == _SIG_DFL {
+		return false
+	}
+
+	// If we aren't handling the signal, forward it.
+	if flags&_SigHandling == 0 {
+		sigfwd(fwdFn, sig, info, ctx)
+		return true
+	}
+
+	// Only forward synchronous signals.
+	c := sigctxt{info, ctx}
+	if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
+		return false
+	}
+	// Determine if the signal occurred inside Go code. We test that:
+	//   (1) we were in a goroutine (i.e., m.curg != nil), and
+	//   (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
+	g := getg()
+	if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
+		return false
+	}
+	// Signal not handled by Go, forward it.
+	if fwdFn != _SIG_IGN {
+		sigfwd(fwdFn, sig, info, ctx)
+	}
+	return true
+}
+
+// msigsave saves the current thread's signal mask into mp.sigmask.
+// This is used to preserve the non-Go signal mask when a non-Go
+// thread calls a Go function.
+// This is nosplit and nowritebarrierrec because it is called by needm
+// which may be called on a non-Go thread with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func msigsave(mp *m) {
+	sigprocmask(_SIG_SETMASK, nil, &mp.sigmask)
+}
+
+// msigrestore sets the current thread's signal mask to sigmask.
+// This is used to restore the non-Go signal mask when a non-Go thread
+// calls a Go function.
+// This is nosplit and nowritebarrierrec because it is called by dropm
+// after g has been cleared.
+//go:nosplit
+//go:nowritebarrierrec
+func msigrestore(sigmask sigset) {
+	sigprocmask(_SIG_SETMASK, &sigmask, nil)
+}
+
+// sigblock blocks all signals in the current thread's signal mask.
+// This is used to block signals while setting up and tearing down g
+// when a non-Go thread calls a Go function.
+// The OS-specific code is expected to define sigset_all.
+// This is nosplit and nowritebarrierrec because it is called by needm
+// which may be called on a non-Go thread with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func sigblock() {
+	var set sigset
+	sigfillset(&set)
+	sigprocmask(_SIG_SETMASK, &set, nil)
+}
+
+// unblocksig removes sig from the current thread's signal mask.
+// This is nosplit and nowritebarrierrec because it is called from
+// dieFromSignal, which can be called by sigfwdgo while running in the
+// signal handler, on the signal stack, with no g available.
+//go:nosplit
+//go:nowritebarrierrec
+func unblocksig(sig uint32) {
+	var set sigset
+	sigemptyset(&set)
+	sigaddset(&set, int(sig))
+	sigprocmask(_SIG_UNBLOCK, &set, nil)
+}
+
+// minitSignals is called when initializing a new m to set the
+// thread's alternate signal stack and signal mask.
+func minitSignals() {
+	minitSignalStack()
+	minitSignalMask()
+}
+
+// minitSignalStack is called when initializing a new m to set the
+// alternate signal stack. If the alternate signal stack is not set
+// for the thread (the normal case) then set the alternate signal
+// stack to the gsignal stack. If the alternate signal stack is set
+// for the thread (the case when a non-Go thread sets the alternate
+// signal stack and then calls a Go function) then set the gsignal
+// stack to the alternate signal stack. Record which choice was made
+// in newSigstack, so that it can be undone in unminit.
+func minitSignalStack() {
+	_g_ := getg()
+	var st _stack_t
+	sigaltstack(nil, &st)
+	if st.ss_flags&_SS_DISABLE != 0 {
+		signalstack(_g_.m.gsignalstack, _g_.m.gsignalstacksize)
+		_g_.m.newSigstack = true
+	} else {
+		_g_.m.newSigstack = false
+	}
+}
+
+// minitSignalMask is called when initializing a new m to set the
+// thread's signal mask. When this is called all signals have been
+// blocked for the thread.  This starts with m.sigmask, which was set
+// either from initSigmask for a newly created thread or by calling
+// msigsave if this is a non-Go thread calling a Go function. It
+// removes all essential signals from the mask, thus causing those
+// signals to not be blocked. Then it sets the thread's signal mask.
+// After this is called the thread can receive signals.
+func minitSignalMask() {
+	nmask := getg().m.sigmask
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			sigdelset(&nmask, i)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
+}
+
+// unminitSignals is called from dropm, via unminit, to undo the
+// effect of calling minit on a non-Go thread.
+//go:nosplit
+func unminitSignals() {
+	if getg().m.newSigstack {
+		signalstack(nil, 0)
+	}
+}