diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 40046113d3f..c25c2e48642 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -cac897bd27885c18a16dacfe27d5efd4526455c5 +449e918b0f93d3e3339edcec21a5bc157f548e54 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index d6fa04b6215..1b0434ed098 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3791,7 +3791,7 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*, this->escapes_ = false; // When compiling the runtime, the address operator does not - // cause local variables to escapes. When escape analysis + // cause local variables to escape. When escape analysis // becomes the default, this should be changed to make it an // error if we have an address operator that escapes. if (gogo->compiling_runtime() && gogo->package_name() == "runtime") diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index a166d1c5c4e..acfab188a80 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -394,6 +394,7 @@ void Gogo::import_package(const std::string& filename, const std::string& local_name, bool is_local_name_exported, + bool must_exist, Location location) { if (filename.empty()) @@ -497,7 +498,8 @@ Gogo::import_package(const std::string& filename, this->relative_import_path_); if (stream == NULL) { - go_error_at(location, "import file %qs not found", filename.c_str()); + if (must_exist) + go_error_at(location, "import file %qs not found", filename.c_str()); return; } @@ -2179,6 +2181,14 @@ Gogo::is_thunk(const Named_object* no) void Gogo::define_global_names() { + if (this->is_main_package()) + { + // Every Go program has to import the runtime package, so that + // it is properly initialized. + this->import_package("runtime", "_", false, false, + Linemap::predeclared_location()); + } + for (Bindings::const_declarations_iterator p = this->globals_->begin_declarations(); p != this->globals_->end_declarations(); diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 17d46d503fd..62bbf9e11ae 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -301,7 +301,7 @@ class Gogo // the declarations are added to the global scope. void import_package(const std::string& filename, const std::string& local_name, - bool is_local_name_exported, Location); + bool is_local_name_exported, bool must_exist, Location); // Whether we are the global binding level. bool diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index b7411d14ffa..34a7765418d 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -5722,7 +5722,7 @@ Parse::import_spec(void*) } this->gogo_->import_package(token->string_value(), local_name, - is_local_name_exported, location); + is_local_name_exported, true, location); this->advance_token(); } diff --git a/libgo/Makefile.am b/libgo/Makefile.am index edf193a5d40..b29f6c4ff57 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -480,14 +480,12 @@ runtime_files = \ runtime/print.c \ runtime/proc.c \ runtime/runtime_c.c \ - runtime/signal_unix.c \ runtime/thread.c \ $(runtime_thread_files) \ runtime/yield.c \ $(rtems_task_variable_add_file) \ malloc.c \ runtime1.c \ - sigqueue.c \ $(runtime_getncpu_file) goc2c.$(OBJEXT): runtime/goc2c.c @@ -504,10 +502,6 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c ./goc2c $< > $@.tmp mv -f $@.tmp $@ -sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c - ./goc2c --go-pkgpath os_signal $< > $@.tmp - mv -f $@.tmp $@ - %.c: $(srcdir)/runtime/%.goc goc2c ./goc2c $< > $@.tmp mv -f $@.tmp $@ @@ -572,6 +566,12 @@ s-runtime_sysinfo: $(srcdir)/mkrsysinfo.sh gen-sysinfo.go $(SHELL) $(srcdir)/mvifdiff.sh tmp-runtime_sysinfo.go runtime_sysinfo.go $(STAMP) $@ +sigtab.go: s-sigtab; @true +s-sigtab: $(srcdir)/mksigtab.sh gen-sysinfo.go + GOOS=$(GOOS) $(SHELL) $(srcdir)/mksigtab.sh > tmp-sigtab.go + $(SHELL) $(srcdir)/mvifdiff.sh tmp-sigtab.go sigtab.go + $(STAMP) $@ + runtime.inc: s-runtime-inc; @true s-runtime-inc: runtime.lo Makefile rm -f runtime.inc.tmp2 @@ -1038,7 +1038,7 @@ $(foreach package,$(PACKAGES),$(eval $(call PACKAGE_template,$(package)))) math_lo_GOCFLAGS = $(MATH_FLAG) # Add the generated file runtime_sysinfo.go to the runtime package. -extra_go_files_runtime = runtime_sysinfo.go +extra_go_files_runtime = runtime_sysinfo.go sigtab.go runtime.lo.dep: $(extra_go_files_runtime) # Add generated files to the syscall package. diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 908e66042a5..44a6999c206 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -203,9 +203,8 @@ am__objects_5 = go-assert.lo go-breakpoint.lo go-caller.lo \ go-unwind.lo go-varargs.lo env_posix.lo heapdump.lo mcache.lo \ mcentral.lo $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo \ msize.lo panic.lo parfor.lo print.lo proc.lo runtime_c.lo \ - signal_unix.lo thread.lo $(am__objects_2) yield.lo \ - $(am__objects_3) malloc.lo runtime1.lo sigqueue.lo \ - $(am__objects_4) + thread.lo $(am__objects_2) yield.lo $(am__objects_3) malloc.lo \ + runtime1.lo $(am__objects_4) am_libgo_llgo_la_OBJECTS = $(am__objects_5) libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS) libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -828,14 +827,12 @@ runtime_files = \ runtime/print.c \ runtime/proc.c \ runtime/runtime_c.c \ - runtime/signal_unix.c \ runtime/thread.c \ $(runtime_thread_files) \ runtime/yield.c \ $(rtems_task_variable_add_file) \ malloc.c \ runtime1.c \ - sigqueue.c \ $(runtime_getncpu_file) noinst_DATA = zstdpkglist.go @@ -1141,7 +1138,7 @@ CHECK_DEPS = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \ math_lo_GOCFLAGS = $(MATH_FLAG) # Add the generated file runtime_sysinfo.go to the runtime package. -extra_go_files_runtime = runtime_sysinfo.go +extra_go_files_runtime = runtime_sysinfo.go sigtab.go # Add generated files to the syscall package. extra_go_files_syscall = \ @@ -1525,8 +1522,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtems-task-variable-add.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runtime1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/runtime_c.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal_unix.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigqueue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread-linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread-sema.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Plo@am__quote@ @@ -1966,13 +1961,6 @@ runtime_c.lo: runtime/runtime_c.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o runtime_c.lo `test -f 'runtime/runtime_c.c' || echo '$(srcdir)/'`runtime/runtime_c.c -signal_unix.lo: runtime/signal_unix.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT signal_unix.lo -MD -MP -MF $(DEPDIR)/signal_unix.Tpo -c -o signal_unix.lo `test -f 'runtime/signal_unix.c' || echo '$(srcdir)/'`runtime/signal_unix.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/signal_unix.Tpo $(DEPDIR)/signal_unix.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/signal_unix.c' object='signal_unix.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o signal_unix.lo `test -f 'runtime/signal_unix.c' || echo '$(srcdir)/'`runtime/signal_unix.c - thread.lo: runtime/thread.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread.lo -MD -MP -MF $(DEPDIR)/thread.Tpo -c -o thread.lo `test -f 'runtime/thread.c' || echo '$(srcdir)/'`runtime/thread.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/thread.Tpo $(DEPDIR)/thread.Plo @@ -3177,10 +3165,6 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c ./goc2c $< > $@.tmp mv -f $@.tmp $@ -sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c - ./goc2c --go-pkgpath os_signal $< > $@.tmp - mv -f $@.tmp $@ - %.c: $(srcdir)/runtime/%.goc goc2c ./goc2c $< > $@.tmp mv -f $@.tmp $@ @@ -3245,6 +3229,12 @@ s-runtime_sysinfo: $(srcdir)/mkrsysinfo.sh gen-sysinfo.go $(SHELL) $(srcdir)/mvifdiff.sh tmp-runtime_sysinfo.go runtime_sysinfo.go $(STAMP) $@ +sigtab.go: s-sigtab; @true +s-sigtab: $(srcdir)/mksigtab.sh gen-sysinfo.go + GOOS=$(GOOS) $(SHELL) $(srcdir)/mksigtab.sh > tmp-sigtab.go + $(SHELL) $(srcdir)/mvifdiff.sh tmp-sigtab.go sigtab.go + $(STAMP) $@ + runtime.inc: s-runtime-inc; @true s-runtime-inc: runtime.lo Makefile rm -f runtime.inc.tmp2 diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go new file mode 100644 index 00000000000..c39107be1b4 --- /dev/null +++ b/libgo/go/runtime/panic.go @@ -0,0 +1,90 @@ +// Copyright 2014 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 + +// Calling panic with one of the errors below will call errorString.Error +// which will call mallocgc to concatenate strings. That will fail if +// malloc is locked, causing a confusing error message. Throw a better +// error message instead. +func panicCheckMalloc(err error) { + gp := getg() + if gp != nil && gp.m != nil && gp.m.mallocing != 0 { + throw(string(err.(errorString))) + } +} + +var indexError = error(errorString("index out of range")) + +func panicindex() { + panicCheckMalloc(indexError) + panic(indexError) +} + +var sliceError = error(errorString("slice bounds out of range")) + +func panicslice() { + panicCheckMalloc(sliceError) + panic(sliceError) +} + +var divideError = error(errorString("integer divide by zero")) + +func panicdivide() { + panicCheckMalloc(divideError) + panic(divideError) +} + +var overflowError = error(errorString("integer overflow")) + +func panicoverflow() { + panicCheckMalloc(overflowError) + panic(overflowError) +} + +var floatError = error(errorString("floating point error")) + +func panicfloat() { + panicCheckMalloc(floatError) + panic(floatError) +} + +var memoryError = error(errorString("invalid memory address or nil pointer dereference")) + +func panicmem() { + panicCheckMalloc(memoryError) + panic(memoryError) +} + +func throwreturn() { + throw("no return at end of a typed function - compiler is broken") +} + +func throwinit() { + throw("recursive call during initialization - linker skew") +} + +//go:nosplit +func canpanic(gp *g) bool { + // Note that g is m->gsignal, different from gp. + // Note also that g->m can change at preemption, so m can go stale + // if this function ever makes a function call. + _g_ := getg() + _m_ := _g_.m + + // Is it okay for gp to panic instead of crashing the program? + // Yes, as long as it is running Go code, not runtime code, + // and not stuck in a system call. + if gp == nil || gp != _m_.curg { + return false + } + if _m_.locks-_m_.softfloat != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { + return false + } + status := readgstatus(gp) + if status&^_Gscan != _Grunning || gp.syscallsp != 0 { + return false + } + return true +} diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go index 37ade7d45b3..f3bbb589d1c 100644 --- a/libgo/go/runtime/runtime2.go +++ b/libgo/go/runtime/runtime2.go @@ -326,8 +326,8 @@ type g struct { m *m // current m; offset known to arm liblink // Not for gccgo: stackAlloc uintptr // stack allocation is [stack.lo,stack.lo+stackAlloc) // Not for gccgo: sched gobuf - // Not for gccgo: syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc - // Not for gccgo: syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc + syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc + syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc // Not for gccgo: stkbar []stkbar // stack barriers, from low to high (see top of mstkbar.go) // Not for gccgo: stkbarPos uintptr // index of lowest stack barrier not hit // Not for gccgo: stktopsp uintptr // expected sp at top of stack, to check in traceback diff --git a/libgo/go/runtime/signal1_unix.go b/libgo/go/runtime/signal1_unix.go new file mode 100644 index 00000000000..f9327651911 --- /dev/null +++ b/libgo/go/runtime/signal1_unix.go @@ -0,0 +1,332 @@ +// 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 darwin dragonfly freebsd linux netbsd openbsd solaris + +package runtime + +import ( + _ "unsafe" // For go:linkname. +) + +// Temporary for gccgo's C code to call: +//go:linkname initsig runtime.initsig +//go:linkname crash runtime.crash +//go:linkname resetcpuprofiler runtime.resetcpuprofiler + +//extern setitimer +func setitimer(which int32, new *_itimerval, old *_itimerval) int32 + +type sigTabT struct { + flags int32 + name string +} + +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 + +// sigmask represents a general signal mask compatible with the GOOS +// specific sigset types: the signal numbered x is represented by bit x-1 +// to match the representation expected by sigprocmask. +type sigmask [(_NSIG + 31) / 32]uint32 + +// 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 { + // 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 := int32(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, funcPC(sigtramp), true) + } +} + +//go:nosplit +//go:nowritebarrierrec +func sigInstallGoHandler(sig int32) 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(int32(sig)) + setsig(int32(sig), funcPC(sigtramp), true) + } + } +} + +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(int32(sig)) { + t.flags &^= _SigHandling + setsig(int32(sig), fwdSig[sig], true) + } + } +} + +func sigignore(sig uint32) { + if sig >= uint32(len(sigtable)) { + return + } + + t := &sigtable[sig] + if t.flags&_SigNotify != 0 { + t.flags &^= _SigHandling + setsig(int32(sig), _SIG_IGN, true) + } +} + +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) +} + +// 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 int32) { + setsig(sig, _SIG_DFL, false) + updatesigmask(sigmask{}) + 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 int32, 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, false) + + // 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) + + // 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, funcPC(sigtramp), true) +} + +func crash() { + dieFromSignal(_SIGABRT) +} + +// 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 sigmask + for i := range sigBlocked { + sigBlocked[i] = ^uint32(0) + } + for i := range sigtable { + if sigtable[i].flags&_SigUnblock != 0 { + sigBlocked[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) + } + } + updatesigmask(sigBlocked) + for { + select { + case sig := <-enableSigChan: + if b := sig - 1; sig > 0 { + sigBlocked[b/32] &^= (1 << (b & 31)) + } + case sig := <-disableSigChan: + if b := sig - 1; sig > 0 { + sigBlocked[b/32] |= (1 << (b & 31)) + } + } + updatesigmask(sigBlocked) + maskUpdatedChan <- struct{}{} + } + }() +} + +// 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() + if !sigsend(uint32(sig)) { + // A foreign thread received the signal sig, and the + // Go code does not want to handle it. + raisebadsignal(int32(sig), c) + } + dropm() +} diff --git a/libgo/go/runtime/signal2_unix.go b/libgo/go/runtime/signal2_unix.go index b7479a71a77..2a39eac43fa 100644 --- a/libgo/go/runtime/signal2_unix.go +++ b/libgo/go/runtime/signal2_unix.go @@ -2,24 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore - -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package runtime import "unsafe" -//go:noescape -func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) - // 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, ctx unsafe.Pointer) bool { +func sigfwdgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) bool { if sig >= uint32(len(sigtable)) { return false } @@ -52,7 +47,7 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool { } // Only forward synchronous signals. - c := &sigctxt{info, ctx} + c := sigctxt{info, ctx} if c.sigcode() == _SI_USER || flags&_SigPanic == 0 { return false } diff --git a/libgo/go/runtime/signal_gccgo.go b/libgo/go/runtime/signal_gccgo.go new file mode 100644 index 00000000000..0ecafccec4c --- /dev/null +++ b/libgo/go/runtime/signal_gccgo.go @@ -0,0 +1,145 @@ +// Copyright 2016 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 darwin dragonfly freebsd linux netbsd openbsd solaris + +package runtime + +import ( + "unsafe" +) + +// Functions for gccgo to support signal handling. In the gc runtime +// these are written in OS-specific files and in assembler. + +//extern sigaction +func sigaction(signum int32, act *_sigaction, oact *_sigaction) int32 + +//extern sigprocmask +func sigprocmask(how int32, set *_sigset_t, oldset *_sigset_t) int32 + +// The argument should be simply *_sigset_t, but that fails on GNU/Linux +// which sometimes uses _sigset_t and sometimes uses ___sigset_t. +//extern sigfillset +func sigfillset(set unsafe.Pointer) int32 + +//extern sigemptyset +func sigemptyset(set *_sigset_t) int32 + +//extern sigaddset +func sigaddset(set *_sigset_t, signum int32) int32 + +//extern sigaltstack +func sigaltstack(ss *_stack_t, oss *_stack_t) int32 + +//extern raise +func raise(sig int32) int32 + +//extern getpid +func getpid() _pid_t + +//extern kill +func kill(pid _pid_t, sig int32) int32 + +type sigctxt struct { + info *_siginfo_t + ctxt unsafe.Pointer +} + +func (c *sigctxt) sigcode() uint64 { return uint64(c.info.si_code) } + +//go:nosplit +func sigblock() { + var set _sigset_t + sigfillset(unsafe.Pointer(&set)) + sigprocmask(_SIG_SETMASK, &set, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsig(i int32, fn uintptr, restart bool) { + var sa _sigaction + sa.sa_flags = _SA_SIGINFO + + // For gccgo we do not set SA_ONSTACK for a signal that can + // cause a panic. Instead, we trust that the split stack has + // enough room to start the signal handler. This is because + // otherwise we have no good way to switch back to the + // original stack before panicing. + if sigtable[i].flags&_SigPanic == 0 { + sa.sa_flags |= _SA_ONSTACK + } + + if restart { + sa.sa_flags |= _SA_RESTART + } + sigfillset(unsafe.Pointer(&sa.sa_mask)) + setSigactionHandler(&sa, fn) + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func setsigstack(i int32) { + var sa _sigaction + sigaction(i, nil, &sa) + handler := getSigactionHandler(&sa) + if handler == 0 || handler == _SIG_DFL || handler == _SIG_IGN || sa.sa_flags&_SA_ONSTACK != 0 { + return + } + if sigtable[i].flags&_SigPanic != 0 { + return + } + sa.sa_flags |= _SA_ONSTACK + sigaction(i, &sa, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func getsig(i int32) uintptr { + var sa _sigaction + if sigaction(i, nil, &sa) < 0 { + // On GNU/Linux glibc rejects attempts to call + // sigaction with signal 32 (SIGCANCEL) or 33 (SIGSETXID). + if GOOS == "linux" && (i == 32 || i == 33) { + return _SIG_DFL + } + throw("sigaction read failure") + } + return getSigactionHandler(&sa) +} + +//go:nosplit +//go:nowritebarrierrec +func updatesigmask(m sigmask) { + var mask _sigset_t + sigemptyset(&mask) + for i := int32(0); i < _NSIG; i++ { + if m[(i-1)/32]&(1<<((uint(i)-1)&31)) != 0 { + sigaddset(&mask, i) + } + } + sigprocmask(_SIG_SETMASK, &mask, nil) +} + +func unblocksig(sig int32) { + var mask _sigset_t + sigemptyset(&mask) + sigaddset(&mask, sig) + sigprocmask(_SIG_UNBLOCK, &mask, nil) +} + +//go:nosplit +//go:nowritebarrierrec +func raiseproc(sig int32) { + kill(getpid(), sig) +} + +//go:nosplit +//go:nowritebarrierrec +func sigfwd(fn uintptr, sig uint32, info *_siginfo_t, ctx unsafe.Pointer) { + f1 := &[1]uintptr{fn} + f2 := *(*func(uint32, *_siginfo_t, unsafe.Pointer))(unsafe.Pointer(&f1)) + f2(sig, info, ctx) +} diff --git a/libgo/go/runtime/signal_sighandler.go b/libgo/go/runtime/signal_sighandler.go new file mode 100644 index 00000000000..3cbec66236f --- /dev/null +++ b/libgo/go/runtime/signal_sighandler.go @@ -0,0 +1,136 @@ +// 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. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package runtime + +import ( + "unsafe" +) + +// crashing is the number of m's we have waited for when implementing +// GOTRACEBACK=crash when a signal is received. +var crashing int32 + +// sighandler is invoked when a signal occurs. The global g will be +// set to a gsignal goroutine and we will be running on the alternate +// signal stack. The parameter g will be the value of the global g +// when the signal occurred. The sig, info, and ctxt parameters are +// from the system signal handler: they are the parameters passed when +// the SA is passed to the sigaction system call. +// +// The garbage collector may have stopped the world, so write barriers +// are not allowed. +// +//go:nowritebarrierrec +func sighandler(sig uint32, info *_siginfo_t, ctxt unsafe.Pointer, gp *g) { + _g_ := getg() + c := sigctxt{info, ctxt} + + if sig == _SIGPROF { + sigprof() + return + } + + sigfault, sigpc := getSiginfo(info, ctxt) + + flags := int32(_SigThrow) + if sig < uint32(len(sigtable)) { + flags = sigtable[sig].flags + } + if c.sigcode() != _SI_USER && flags&_SigPanic != 0 { + // Emulate gc by passing arguments out of band, + // although we don't really have to. + gp.sig = sig + gp.sigcode0 = uintptr(c.sigcode()) + gp.sigcode1 = sigfault + gp.sigpc = sigpc + + setg(gp) + + // All signals were blocked due to the sigaction mask; + // unblock them. + var set _sigset_t + sigfillset(unsafe.Pointer(&set)) + sigprocmask(_SIG_UNBLOCK, &set, nil) + + sigpanic() + throw("sigpanic returned") + } + + if c.sigcode() == _SI_USER || flags&_SigNotify != 0 { + if sigsend(sig) { + return + } + } + + if c.sigcode() == _SI_USER && signal_ignored(sig) { + return + } + + if flags&_SigKill != 0 { + dieFromSignal(int32(sig)) + } + + if flags&_SigThrow == 0 { + return + } + + _g_.m.throwing = 1 + _g_.m.caughtsig.set(gp) + + if crashing == 0 { + startpanic() + } + + if sig < uint32(len(sigtable)) { + print(sigtable[sig].name, "\n") + } else { + print("Signal ", sig, "\n") + } + + if sigpc != 0 { + print("PC=", hex(sigpc), " ") + } + print("m=", _g_.m.id, " sigcode=", c.sigcode(), "\n") + if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 { + print("signal arrived during cgo execution\n") + gp = _g_.m.lockedg + } + print("\n") + + level, _, docrash := gotraceback() + if level > 0 { + goroutineheader(gp) + traceback(0) + if crashing == 0 { + tracebackothers(gp) + print("\n") + } + dumpregs(info, ctxt) + } + + if docrash { + crashing++ + if crashing < mcount() { + // There are other m's that need to dump their stacks. + // Relay SIGQUIT to the next m by sending it to the current process. + // All m's that have already received SIGQUIT have signal masks blocking + // receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet. + // When the last m receives the SIGQUIT, it will fall through to the call to + // crash below. Just in case the relaying gets botched, each m involved in + // the relay sleeps for 5 seconds and then does the crash/exit itself. + // In expected operation, the last m has received the SIGQUIT and run + // crash/exit and the process is gone, all long before any of the + // 5-second sleeps have finished. + print("\n-----\n\n") + raiseproc(_SIGQUIT) + usleep(5 * 1000 * 1000) + } + crash() + } + + exit(2) +} diff --git a/libgo/go/runtime/signal_sigtramp.go b/libgo/go/runtime/signal_sigtramp.go index f4a6ebd71c1..67b9e670433 100644 --- a/libgo/go/runtime/signal_sigtramp.go +++ b/libgo/go/runtime/signal_sigtramp.go @@ -2,19 +2,20 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore - -// +build dragonfly linux netbsd +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package runtime import "unsafe" +// For gccgo, use go:linkname so the C signal handler can call this one. +//go:linkname sigtrampgo runtime.sigtrampgo + // Continuation of the (assembly) sigtramp() logic. // This may be called with the world stopped. //go:nosplit //go:nowritebarrierrec -func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { +func sigtrampgo(sig uint32, info *_siginfo_t, ctx unsafe.Pointer) { if sigfwdgo(sig, info, ctx) { return } @@ -32,28 +33,6 @@ func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) { return } - // If some non-Go code called sigaltstack, adjust. - sp := uintptr(unsafe.Pointer(&sig)) - if sp < g.m.gsignal.stack.lo || sp >= g.m.gsignal.stack.hi { - var st sigaltstackt - sigaltstack(nil, &st) - if st.ss_flags&_SS_DISABLE != 0 { - setg(nil) - cgocallback(unsafe.Pointer(funcPC(noSignalStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0) - } - stsp := uintptr(unsafe.Pointer(st.ss_sp)) - if sp < stsp || sp >= stsp+st.ss_size { - setg(nil) - cgocallback(unsafe.Pointer(funcPC(sigNotOnStack)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig), 0) - } - g.m.gsignal.stack.lo = stsp - g.m.gsignal.stack.hi = stsp + st.ss_size - g.m.gsignal.stackguard0 = stsp + _StackGuard - g.m.gsignal.stackguard1 = stsp + _StackGuard - g.m.gsignal.stackAlloc = st.ss_size - g.m.gsignal.stktopsp = getcallersp(unsafe.Pointer(&sig)) - } - setg(g.m.gsignal) sighandler(sig, info, ctx, g) setg(g) diff --git a/libgo/go/runtime/signal_unix.go b/libgo/go/runtime/signal_unix.go new file mode 100644 index 00000000000..f59c9b95497 --- /dev/null +++ b/libgo/go/runtime/signal_unix.go @@ -0,0 +1,21 @@ +// 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 darwin dragonfly freebsd linux netbsd openbsd solaris + +package runtime + +import _ "unsafe" // for go:linkname + +//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 +} diff --git a/libgo/go/runtime/sigpanic_unix.go b/libgo/go/runtime/sigpanic_unix.go new file mode 100644 index 00000000000..00ad090f428 --- /dev/null +++ b/libgo/go/runtime/sigpanic_unix.go @@ -0,0 +1,48 @@ +// Copyright 2014 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 darwin dragonfly freebsd linux netbsd openbsd solaris + +package runtime + +import _ "unsafe" // For go:linkname. + +// For gccgo, C code has to call sigpanic, so we have to export it. +//go:linkname sigpanic runtime.sigpanic + +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 || 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 || 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)) +} diff --git a/libgo/go/runtime/sigqueue.go b/libgo/go/runtime/sigqueue.go new file mode 100644 index 00000000000..a6d498f9b03 --- /dev/null +++ b/libgo/go/runtime/sigqueue.go @@ -0,0 +1,178 @@ +// Copyright 2009 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. + +// This file implements runtime support for signal handling. +// +// Most synchronization primitives are not available from +// the signal handler (it cannot block, allocate memory, or use locks) +// so the handler communicates with a processing goroutine +// via struct sig, below. +// +// sigsend is called by the signal handler to queue a new signal. +// signal_recv is called by the Go program to receive a newly queued signal. +// Synchronization between sigsend and signal_recv is based on the sig.state +// variable. It can be in 3 states: sigIdle, sigReceiving and sigSending. +// sigReceiving means that signal_recv is blocked on sig.Note and there are no +// new pending signals. +// sigSending means that sig.mask *may* contain new pending signals, +// signal_recv can't be blocked in this state. +// sigIdle means that there are no new pending signals and signal_recv is not blocked. +// Transitions between states are done atomically with CAS. +// When signal_recv is unblocked, it resets sig.Note and rechecks sig.mask. +// If several sigsends and signal_recv execute concurrently, it can lead to +// unnecessary rechecks of sig.mask, but it cannot lead to missed signals +// nor deadlocks. + +// +build !plan9 + +package runtime + +import ( + "runtime/internal/atomic" + _ "unsafe" // for go:linkname +) + +var sig struct { + note note + mask [(_NSIG + 31) / 32]uint32 + wanted [(_NSIG + 31) / 32]uint32 + ignored [(_NSIG + 31) / 32]uint32 + recv [(_NSIG + 31) / 32]uint32 + state uint32 + inuse bool +} + +const ( + sigIdle = iota + sigReceiving + sigSending +) + +// Called from sighandler to send a signal back out of the signal handling thread. +// Reports whether the signal was sent. If not, the caller typically crashes the program. +func sigsend(s uint32) bool { + bit := uint32(1) << uint(s&31) + if !sig.inuse || s >= uint32(32*len(sig.wanted)) || sig.wanted[s/32]&bit == 0 { + return false + } + + // Add signal to outgoing queue. + for { + mask := sig.mask[s/32] + if mask&bit != 0 { + return true // signal already in queue + } + if atomic.Cas(&sig.mask[s/32], mask, mask|bit) { + break + } + } + + // Notify receiver that queue has new bit. +Send: + for { + switch atomic.Load(&sig.state) { + default: + throw("sigsend: inconsistent state") + case sigIdle: + if atomic.Cas(&sig.state, sigIdle, sigSending) { + break Send + } + case sigSending: + // notification already pending + break Send + case sigReceiving: + if atomic.Cas(&sig.state, sigReceiving, sigIdle) { + notewakeup(&sig.note) + break Send + } + } + } + + return true +} + +// Called to receive the next queued signal. +// Must only be called from a single goroutine at a time. +//go:linkname signal_recv os_signal.signal_recv +func signal_recv() uint32 { + for { + // Serve any signals from local copy. + for i := uint32(0); i < _NSIG; i++ { + if sig.recv[i/32]&(1<<(i&31)) != 0 { + sig.recv[i/32] &^= 1 << (i & 31) + return i + } + } + + // Wait for updates to be available from signal sender. + Receive: + for { + switch atomic.Load(&sig.state) { + default: + throw("signal_recv: inconsistent state") + case sigIdle: + if atomic.Cas(&sig.state, sigIdle, sigReceiving) { + notetsleepg(&sig.note, -1) + noteclear(&sig.note) + break Receive + } + case sigSending: + if atomic.Cas(&sig.state, sigSending, sigIdle) { + break Receive + } + } + } + + // Incorporate updates from sender into local copy. + for i := range sig.mask { + sig.recv[i] = atomic.Xchg(&sig.mask[i], 0) + } + } +} + +// Must only be called from a single goroutine at a time. +//go:linkname signal_enable os_signal.signal_enable +func signal_enable(s uint32) { + if !sig.inuse { + // The first call to signal_enable is for us + // to use for initialization. It does not pass + // signal information in m. + sig.inuse = true // enable reception of signals; cannot disable + noteclear(&sig.note) + return + } + + if s >= uint32(len(sig.wanted)*32) { + return + } + sig.wanted[s/32] |= 1 << (s & 31) + sig.ignored[s/32] &^= 1 << (s & 31) + sigenable(s) +} + +// Must only be called from a single goroutine at a time. +//go:linkname signal_disable os_signal.signal_disable +func signal_disable(s uint32) { + if s >= uint32(len(sig.wanted)*32) { + return + } + sig.wanted[s/32] &^= 1 << (s & 31) + sigdisable(s) +} + +// Must only be called from a single goroutine at a time. +//go:linkname signal_ignore os_signal.signal_ignore +func signal_ignore(s uint32) { + if s >= uint32(len(sig.wanted)*32) { + return + } + sig.wanted[s/32] &^= 1 << (s & 31) + sig.ignored[s/32] |= 1 << (s & 31) + sigignore(s) +} + +// Checked by signal handlers. +func signal_ignored(s uint32) bool { + return sig.ignored[s/32]&(1<<(s&31)) != 0 +} diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index 755933de713..d0641ed7150 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -241,9 +241,15 @@ func newarray(*_type, int) unsafe.Pointer // funcPC returns the entry PC of the function f. // It assumes that f is a func value. Otherwise the behavior is undefined. // For gccgo here unless and until we port proc.go. +// Note that this differs from the gc implementation; the gc implementation +// adds sys.PtrSize to the address of the interface value, but GCC's +// alias analysis decides that that can not be a reference to the second +// field of the interface, and in some cases it drops the initialization +// of the second field as a dead store. //go:nosplit func funcPC(f interface{}) uintptr { - return **(**uintptr)(add(unsafe.Pointer(&f), sys.PtrSize)) + i := (*iface)(unsafe.Pointer(&f)) + return **(**uintptr)(i.data) } // typedmemmove copies a typed value. @@ -489,3 +495,27 @@ var zerobase uintptr func getZerobase() *uintptr { return &zerobase } + +// Temporary for gccgo until we port proc.go. +func needm() +func dropm() +func sigprof() +func mcount() int32 + +// Signal trampoline, written in C. +func sigtramp() + +// The sa_handler field is generally hidden in a union, so use C accessors. +func getSigactionHandler(*_sigaction) uintptr +func setSigactionHandler(*_sigaction, uintptr) + +// Retrieve fields from the siginfo_t and ucontext_t pointers passed +// to a signal handler using C, as they are often hidden in a union. +// Returns and, if available, PC where signal occurred. +func getSiginfo(*_siginfo_t, unsafe.Pointer) (sigaddr uintptr, sigpc uintptr) + +// Implemented in C for gccgo. +func dumpregs(*_siginfo_t, unsafe.Pointer) + +// Temporary for gccgo until we port panic.go. +func startpanic() diff --git a/libgo/mkrsysinfo.sh b/libgo/mkrsysinfo.sh index 32799d6251b..1ab4ea40f71 100755 --- a/libgo/mkrsysinfo.sh +++ b/libgo/mkrsysinfo.sh @@ -41,6 +41,11 @@ echo $timeval | \ sed -e 's/type _timeval /type timeval /' \ -e 's/tv_sec *[a-zA-Z0-9_]*/tv_sec timeval_sec_t/' \ -e 's/tv_usec *[a-zA-Z0-9_]*/tv_usec timeval_usec_t/' >> ${OUT} +echo >> ${OUT} +echo "func (tv *timeval) set_usec(x int32) {" >> ${OUT} +echo " tv.tv_usec = timeval_usec_t(x)" >> ${OUT} +echo "}" >> ${OUT} + timespec=`grep '^type _timespec ' gen-sysinfo.go || true` if test "$timespec" = ""; then # IRIX 6.5 has __timespec instead. diff --git a/libgo/mksigtab.sh b/libgo/mksigtab.sh new file mode 100644 index 00000000000..d33b7e2ff0b --- /dev/null +++ b/libgo/mksigtab.sh @@ -0,0 +1,98 @@ +#!/bin/sh + +# Copyright 2016 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. + +# Create sigtab.go from gen-sysinfo.go. + +# This shell scripts creates the sigtab.go file, which maps signals to +# their dispositions. We generate a file so that we can build a +# composite literal that only refers to signals that are defined on +# this system. + +# This script simply writes to standard output. + +set -e + +echo '// Generated by mksigtab.sh. Do not edit.' +echo +echo 'package runtime' +echo +echo 'var sigtable = [...]sigTabT{' + +# Handle signals valid on all Unix systems. + +echo ' 0: {0, "SIGNONE: no trap"},' +echo ' _SIGHUP: {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},' +echo ' _SIGINT: {_SigNotify + _SigKill, "SIGINT: interrupt"},' +echo ' _SIGQUIT: {_SigNotify + _SigThrow, "SIGQUIT: quit"},' +echo ' _SIGILL: {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},' +echo ' _SIGTRAP: {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},' +echo ' _SIGABRT: {_SigNotify + _SigThrow, "SIGABRT: abort"},' +echo ' _SIGBUS: {_SigPanic + _SigUnblock, "SIGBUS: bus error"},' +echo ' _SIGFPE: {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},' +echo ' _SIGKILL: {0, "SIGKILL: kill"},' +echo ' _SIGUSR1: {_SigNotify, "SIGUSR1: user-defined signal 1"},' +echo ' _SIGSEGV: {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},' +echo ' _SIGUSR2: {_SigNotify, "SIGUSR2: user-defined signal 2"},' +echo ' _SIGPIPE: {_SigNotify, "SIGPIPE: write to broken pipe"},' +echo ' _SIGALRM: {_SigNotify, "SIGALRM: alarm clock"},' +echo ' _SIGTERM: {_SigNotify + _SigKill, "SIGTERM: termination"},' +echo ' _SIGCHLD: {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},' +echo ' _SIGCONT: {_SigNotify + _SigDefault, "SIGCONT: continue"},' +echo ' _SIGSTOP: {0, "SIGSTOP: stop"},' +echo ' _SIGTSTP: {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},' +echo ' _SIGTTIN: {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},' +echo ' _SIGTTOU: {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},' +echo ' _SIGURG: {_SigNotify, "SIGURG: urgent condition on socket"},' +echo ' _SIGXCPU: {_SigNotify, "SIGXCPU: cpu limit exceeded"},' +echo ' _SIGXFSZ: {_SigNotify, "SIGXFSZ: file size limit exceeded"},' +echo ' _SIGVTALRM: {_SigNotify, "SIGVTALRM: virtual alarm clock"},' +echo ' _SIGPROF: {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},' +echo ' _SIGWINCH: {_SigNotify, "SIGWINCH: window size change"},' +echo ' _SIGSYS: {_SigThrow, "SIGSYS: bad system call"},' + +# Handle signals that are not supported on all systems. + +checksig() { + if grep 'const $1 = ' gen-sysinfo.go >/dev/null 2>&1; then + echo " $1: $2," + fi +} + +checksig _SIGSTKFLT ' {_SigThrow + _SigUnblock, "SIGSTKFLT: stack fault"}' +checksig _SIGIO ' {_SigNotify, "SIGIO: i/o now possible"}' +checksig _SIGPWR ' {_SigNotify, "SIGPWR: power failure restart"}' +checksig _SIGEMT ' {_SigThrow, "SIGEMT: emulate instruction executed"}' +checksig _SIGINFO ' {_SigNotify, "SIGINFO: status request from keyboard"}' +checksig _SIGTHR ' {_SigNotify, "SIGTHR: reserved"}' +checksig _SIGPOLL ' {_SigNotify, "SIGPOLL: pollable event occurred"}' +checksig _SIGWAITING '{_SigNotify, "SIGWAITING: reserved signal no longer used by"}' +checksig _SIGLWP ' {_SigNotify, "SIGLWP: reserved signal no longer used by"}' +checksig _SIGFREEZE ' {_SigNotify, "SIGFREEZE: special signal used by CPR"}' +checksig _SIGTHAW ' {_SigNotify, "SIGTHAW: special signal used by CPR"}' +checksig _SIGCANCEL ' {_SigSetStack + _SigUnblock, "SIGCANCEL: reserved signal for thread cancellation"}' +checksig _SIGXRES ' {_SigNotify, "SIGXRES: resource control exceeded"}' +checksig _SIGJVM1 ' {_SigNotify, "SIGJVM1: reserved signal for Java Virtual Machine"}' +checksig _SIGJVM2 ' {_SigNotify, "SIGJVM2: reserved signal for Java Virtual Machine"}' + +# Special handling of signals 32 and 33 on GNU/Linux systems, +# because they are special to glibc. +if test "${GOOS}" = "linux"; then + echo ' 32: {_SigSetStack + _SigUnblock, "signal 32"}, /* SIGCANCEL; see issue 6997 */' + echo ' 33: {_SigSetStack + _SigUnblock, "signal 33"}, /* SIGSETXID; see issues 3871, 9400, 12498 */' +fi + +nsig=`grep 'const _*NSIG = [0-9]*$' gen-sysinfo.go | sed -e 's/.* = \([0-9]*\)/\1/'` +i=1 +while test "$i" -lt "$nsig"; do + if ! grep "const _SIG.* = $i" gen-sysinfo.go >/dev/null 2>&1; then + if test "${GOOS}" != "linux" || test "$i" -ne 32 -a "$i" -ne 33; then + echo " $i: {_SigNotify, \"signal $i\"}," + fi + fi + i=`expr "$i" + 1` +done + +echo '}' diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index e3e4a198566..9ee02a34f8f 100644 --- a/libgo/runtime/go-signal.c +++ b/libgo/runtime/go-signal.c @@ -8,11 +8,11 @@ #include #include #include +#include #include "runtime.h" #include "go-assert.h" #include "go-panic.h" -#include "signal_unix.h" #ifndef SA_RESTART #define SA_RESTART 0 @@ -24,530 +24,257 @@ extern void __splitstack_getcontext(void *context[10]); extern void __splitstack_setcontext(void *context[10]); +extern void *__splitstack_find_context(void *context[10], size_t *, + void **, void **, void **); + #endif -#define N _SigNotify -#define K _SigKill -#define T _SigThrow -#define P _SigPanic -#define D _SigDefault +// The rest of the signal handler, written in Go. -/* Signal actions. This collects the sigtab tables for several - different targets from the master library. SIGKILL and SIGSTOP are - not listed, as we don't want to set signal handlers for them. */ +extern void sigtrampgo(uint32, siginfo_t *, void *) + __asm__(GOSYM_PREFIX "runtime.sigtrampgo"); -SigTab runtime_sigtab[] = { -#ifdef SIGHUP - { SIGHUP, N + K, NULL }, -#endif -#ifdef SIGINT - { SIGINT, N + K, NULL }, -#endif -#ifdef SIGQUIT - { SIGQUIT, N + T, NULL }, -#endif -#ifdef SIGILL - { SIGILL, T, NULL }, -#endif -#ifdef SIGTRAP - { SIGTRAP, T, NULL }, -#endif -#ifdef SIGABRT - { SIGABRT, N + T, NULL }, -#endif -#ifdef SIGBUS - { SIGBUS, P, NULL }, -#endif -#ifdef SIGFPE - { SIGFPE, P, NULL }, -#endif -#ifdef SIGUSR1 - { SIGUSR1, N, NULL }, -#endif -#ifdef SIGSEGV - { SIGSEGV, P, NULL }, -#endif -#ifdef SIGUSR2 - { SIGUSR2, N, NULL }, -#endif -#ifdef SIGPIPE - { SIGPIPE, N, NULL }, -#endif -#ifdef SIGALRM - { SIGALRM, N, NULL }, -#endif -#ifdef SIGTERM - { SIGTERM, N + K, NULL }, -#endif -#ifdef SIGSTKFLT - { SIGSTKFLT, T, NULL }, -#endif -#ifdef SIGCHLD - { SIGCHLD, N, NULL }, -#endif -#ifdef SIGCONT - { SIGCONT, N + D, NULL }, -#endif -#ifdef SIGTSTP - { SIGTSTP, N + D, NULL }, -#endif -#ifdef SIGTTIN - { SIGTTIN, N + D, NULL }, -#endif -#ifdef SIGTTOU - { SIGTTOU, N + D, NULL }, -#endif -#ifdef SIGURG - { SIGURG, N, NULL }, -#endif -#ifdef SIGXCPU - { SIGXCPU, N, NULL }, -#endif -#ifdef SIGXFSZ - { SIGXFSZ, N, NULL }, -#endif -#ifdef SIGVTALRM - { SIGVTALRM, N, NULL }, -#endif -#ifdef SIGPROF - { SIGPROF, N, NULL }, -#endif -#ifdef SIGWINCH - { SIGWINCH, N, NULL }, -#endif -#ifdef SIGIO - { SIGIO, N, NULL }, -#endif -#ifdef SIGPWR - { SIGPWR, N, NULL }, -#endif -#ifdef SIGSYS - { SIGSYS, N, NULL }, -#endif -#ifdef SIGEMT - { SIGEMT, T, NULL }, -#endif -#ifdef SIGINFO - { SIGINFO, N, NULL }, -#endif -#ifdef SIGTHR - { SIGTHR, N, NULL }, -#endif - { -1, 0, NULL } +// The Go signal handler, written in C. This should be running on the +// alternate signal stack. This is responsible for setting up the +// split stack context so that stack guard checks will work as +// expected. + +void sigtramp(int, siginfo_t *, void *) + __attribute__ ((no_split_stack)); + +void sigtramp(int, siginfo_t *, void *) + __asm__ (GOSYM_PREFIX "runtime.sigtramp"); + +#ifndef USING_SPLIT_STACK + +// When not using split stacks, there are no stack checks, and there +// is nothing special for this function to do. + +void +sigtramp(int sig, siginfo_t *info, void *context) +{ + sigtrampgo(sig, info, context); +} + +#else // USING_SPLIT_STACK + +void +sigtramp(int sig, siginfo_t *info, void *context) +{ + G *gp; + void *stack_context[10]; + void *stack; + size_t stack_size; + void *next_segment; + void *next_sp; + void *initial_sp; + uintptr sp; + stack_t st; + uintptr stsp; + + gp = runtime_g(); + + if (gp == nil) { + // Let the Go code handle this case. + // It should only call nosplit functions in this case. + sigtrampgo(sig, info, context); + return; + } + + // If this signal is one for which we will panic, we are not + // on the alternate signal stack. It's OK to call split-stack + // functions here. + if (sig == SIGBUS || sig == SIGFPE || sig == SIGSEGV) { + sigtrampgo(sig, info, context); + return; + } + + // We are running on the alternate signal stack. + + __splitstack_getcontext(&stack_context[0]); + + stack = __splitstack_find_context(&gp->m->gsignal->stackcontext[0], + &stack_size, &next_segment, + &next_sp, &initial_sp); + + // If some non-Go code called sigaltstack, adjust. + sp = (uintptr)(&stack_size); + if (sp < (uintptr)(stack) || sp >= (uintptr)(stack) + stack_size) { + sigaltstack(nil, &st); + if ((st.ss_flags & SS_DISABLE) != 0) { + runtime_printf("signal %d received on thread with no signal stack\n", (int32)(sig)); + runtime_throw("non-Go code disabled sigaltstack"); + } + + stsp = (uintptr)(st.ss_sp); + if (sp < stsp || sp >= stsp + st.ss_size) { + runtime_printf("signal %d received but handler not on signal stack\n", (int32)(sig)); + runtime_throw("non-Go code set up signal handler without SA_ONSTACK flag"); + } + + // Unfortunately __splitstack_find_context will return NULL + // when it is called on a context that has never been used. + // There isn't much we can do but assume all is well. + if (stack != NULL) { + // Here the gc runtime adjusts the gsignal + // stack guard to match the values returned by + // sigaltstack. Unfortunately we have no way + // to do that. + runtime_printf("signal %d received on unknown signal stack\n", (int32)(sig)); + runtime_throw("non-Go code changed signal stack"); + } + } + + // Set the split stack context so that the stack guards are + // checked correctly. + + __splitstack_setcontext(&gp->m->gsignal->stackcontext[0]); + + sigtrampgo(sig, info, context); + + // We are going to return back to the signal trampoline and + // then to whatever we were doing before we got the signal. + // Restore the split stack context so that stack guards are + // checked correctly. + + __splitstack_setcontext(&stack_context[0]); +} + +#endif // USING_SPLIT_STACK + +// C code to manage the sigaction sa_sigaction field, which is +// typically a union and so hard for mksysinfo.sh to handle. + +uintptr getSigactionHandler(struct sigaction*) + __attribute__ ((no_split_stack)); + +uintptr getSigactionHandler(struct sigaction*) + __asm__ (GOSYM_PREFIX "runtime.getSigactionHandler"); + +uintptr +getSigactionHandler(struct sigaction* sa) +{ + return (uintptr)(sa->sa_sigaction); +} + +void setSigactionHandler(struct sigaction*, uintptr) + __attribute__ ((no_split_stack)); + +void setSigactionHandler(struct sigaction*, uintptr) + __asm__ (GOSYM_PREFIX "runtime.setSigactionHandler"); + +void +setSigactionHandler(struct sigaction* sa, uintptr handler) +{ + sa->sa_sigaction = (void*)(handler); +} + +// C code to fetch values from the siginfo_t and ucontext_t pointers +// passed to a signal handler. + +struct getSiginfoRet { + uintptr sigaddr; + uintptr sigpc; }; -#undef N -#undef K -#undef T -#undef P -#undef D -/* Handle a signal, for cases where we don't panic. We can split the - stack here. */ +struct getSiginfoRet getSiginfo(siginfo_t *, void *) + __asm__(GOSYM_PREFIX "runtime.getSiginfo"); -void -runtime_sighandler (int sig, Siginfo *info, - void *context __attribute__ ((unused)), G *gp) +struct getSiginfoRet +getSiginfo(siginfo_t *info, void *context __attribute__((unused))) { - M *m; - int i; + struct getSiginfoRet ret; + Location loc[1]; + int32 n; - m = runtime_m (); + ret.sigaddr = (uintptr)(info->si_addr); + ret.sigpc = 0; -#ifdef SIGPROF - if (sig == SIGPROF) - { - /* FIXME: Handle m == NULL by calling something like gc's - sigprofNonGo. */ - if (m != NULL && gp != m->g0 && gp != m->gsignal) - runtime_sigprof (); - return; - } + // There doesn't seem to be a portable way to get the PC. + // Use unportable code to pull it from context, and if that fails + // try a stack backtrace across the signal handler. + +#ifdef __x86_64__ + #ifdef __linux__ + ret.sigpc = ((ucontext_t*)(context))->uc_mcontext.gregs[REG_RIP]; + #endif +#endif +#ifdef __i386__ + #ifdef __linux__ + ret.sigpc = ((ucontext_t*)(context))->uc_mcontext.gregs[REG_EIP]; + #endif #endif - if (m == NULL) - { - runtime_badsignal (sig); - return; - } - - for (i = 0; runtime_sigtab[i].sig != -1; ++i) - { - SigTab *t; - bool notify, crash; - - t = &runtime_sigtab[i]; - - if (t->sig != sig) - continue; - - notify = false; -#ifdef SA_SIGINFO - notify = info != NULL && info->si_code == SI_USER; -#endif - if (notify || (t->flags & _SigNotify) != 0) - { - if (__go_sigsend (sig)) - return; - } - if ((t->flags & _SigKill) != 0) - runtime_exit (2); - if ((t->flags & _SigThrow) == 0) - return; - - runtime_startpanic (); - - { - const char *name = NULL; - -#ifdef HAVE_STRSIGNAL - name = strsignal (sig); -#endif - - if (name == NULL) - runtime_printf ("Signal %d\n", sig); - else - runtime_printf ("%s\n", name); - } - - if (m->lockedg != NULL && m->ncgo > 0 && gp == m->g0) - { - runtime_printf("signal arrived during cgo execution\n"); - gp = m->lockedg; + if (ret.sigpc == 0) { + // Skip getSiginfo/sighandler/sigtrampgo/sigtramp/handler. + n = runtime_callers(5, &loc[0], 1, false); + if (n > 0) { + ret.sigpc = loc[0].pc; + } } - runtime_printf ("\n"); + return ret; +} - if (runtime_gotraceback (&crash)) +// Dump registers when crashing in a signal. +// There is no portable way to write this, +// so we just have some CPU/OS specific implementations. + +void dumpregs(siginfo_t *, void *) + __asm__(GOSYM_PREFIX "runtime.dumpregs"); + +void +dumpregs(siginfo_t *info __attribute__((unused)), void *context __attribute__((unused))) +{ +#ifdef __x86_64__ + #ifdef __linux__ { - G *g; + mcontext_t *m = &((ucontext_t*)(context))->uc_mcontext; - g = runtime_g (); - runtime_traceback (0); - runtime_tracebackothers (g); - - /* The gc library calls runtime_dumpregs here, and provides - a function that prints the registers saved in context in - a readable form. */ - } - - if (crash) - runtime_crash (); - - runtime_exit (2); - } - - __builtin_unreachable (); -} - -/* The start of handling a signal which panics. */ - -static void -sig_panic_leadin (G *gp) -{ - int i; - sigset_t clear; - - if (!runtime_canpanic (gp)) - runtime_throw ("unexpected signal during runtime execution"); - - /* The signal handler blocked signals; unblock them. */ - i = sigfillset (&clear); - __go_assert (i == 0); - i = pthread_sigmask (SIG_UNBLOCK, &clear, NULL); - __go_assert (i == 0); -} - -#ifdef SA_SIGINFO - -/* Signal dispatch for signals which panic, on systems which support - SA_SIGINFO. This is called on the thread stack, and as such it is - permitted to split the stack. */ - -static void -sig_panic_info_handler (int sig, Siginfo *info, void *context) -{ - G *g; - - g = runtime_g (); - if (g == NULL || info->si_code == SI_USER) - { - runtime_sighandler (sig, info, context, g); - return; - } - - g->sig = sig; - g->sigcode0 = info->si_code; - g->sigcode1 = (uintptr_t) info->si_addr; - - /* It would be nice to set g->sigpc here as the gc library does, but - I don't know how to get it portably. */ - - sig_panic_leadin (g); - - switch (sig) - { -#ifdef SIGBUS - case SIGBUS: - if ((info->si_code == BUS_ADRERR && (uintptr_t) info->si_addr < 0x1000) - || g->paniconfault) - runtime_panicstring ("invalid memory address or " - "nil pointer dereference"); - runtime_printf ("unexpected fault address %p\n", info->si_addr); - runtime_throw ("fault"); + runtime_printf("rax %X\n", m->gregs[REG_RAX]); + runtime_printf("rbx %X\n", m->gregs[REG_RBX]); + runtime_printf("rcx %X\n", m->gregs[REG_RCX]); + runtime_printf("rdx %X\n", m->gregs[REG_RDX]); + runtime_printf("rdi %X\n", m->gregs[REG_RDI]); + runtime_printf("rsi %X\n", m->gregs[REG_RSI]); + runtime_printf("rbp %X\n", m->gregs[REG_RBP]); + runtime_printf("rsp %X\n", m->gregs[REG_RSP]); + runtime_printf("r8 %X\n", m->gregs[REG_R8]); + runtime_printf("r9 %X\n", m->gregs[REG_R9]); + runtime_printf("r10 %X\n", m->gregs[REG_R10]); + runtime_printf("r11 %X\n", m->gregs[REG_R11]); + runtime_printf("r12 %X\n", m->gregs[REG_R12]); + runtime_printf("r13 %X\n", m->gregs[REG_R13]); + runtime_printf("r14 %X\n", m->gregs[REG_R14]); + runtime_printf("r15 %X\n", m->gregs[REG_R15]); + runtime_printf("rip %X\n", m->gregs[REG_RIP]); + runtime_printf("rflags %X\n", m->gregs[REG_EFL]); + runtime_printf("cs %X\n", m->gregs[REG_CSGSFS] & 0xffff); + runtime_printf("fs %X\n", (m->gregs[REG_CSGSFS] >> 16) & 0xffff); + runtime_printf("gs %X\n", (m->gregs[REG_CSGSFS] >> 32) & 0xffff); + } + #endif #endif -#ifdef SIGSEGV - case SIGSEGV: - if (((info->si_code == 0 - || info->si_code == SEGV_MAPERR - || info->si_code == SEGV_ACCERR) - && (uintptr_t) info->si_addr < 0x1000) - || g->paniconfault) - runtime_panicstring ("invalid memory address or " - "nil pointer dereference"); - runtime_printf ("unexpected fault address %p\n", info->si_addr); - runtime_throw ("fault"); -#endif - -#ifdef SIGFPE - case SIGFPE: - switch (info->si_code) +#ifdef __i386__ + #ifdef __linux__ { - case FPE_INTDIV: - runtime_panicstring ("integer divide by zero"); - case FPE_INTOVF: - runtime_panicstring ("integer overflow"); - } - runtime_panicstring ("floating point error"); -#endif - } + mcontext_t *m = &((ucontext_t*)(context))->uc_mcontext; - /* All signals with _SigPanic should be in cases above, and this - handler should only be invoked for those signals. */ - __builtin_unreachable (); -} - -#else /* !defined (SA_SIGINFO) */ - -static void -sig_panic_handler (int sig) -{ - G *g; - - g = runtime_g (); - if (g == NULL) - { - runtime_sighandler (sig, NULL, NULL, g); - return; - } - - g->sig = sig; - g->sigcode0 = 0; - g->sigcode1 = 0; - - sig_panic_leadin (g); - - switch (sig) - { -#ifdef SIGBUS - case SIGBUS: - runtime_panicstring ("invalid memory address or " - "nil pointer dereference"); -#endif - -#ifdef SIGSEGV - case SIGSEGV: - runtime_panicstring ("invalid memory address or " - "nil pointer dereference"); -#endif - -#ifdef SIGFPE - case SIGFPE: - runtime_panicstring ("integer divide by zero or floating point error"); -#endif - } - - /* All signals with _SigPanic should be in cases above, and this - handler should only be invoked for those signals. */ - __builtin_unreachable (); -} - -#endif /* !defined (SA_SIGINFO) */ - -/* A signal handler used for signals which are not going to panic. - This is called on the alternate signal stack so it may not split - the stack. */ - -static void -sig_tramp_info (int, Siginfo *, void *) __attribute__ ((no_split_stack)); - -static void -sig_tramp_info (int sig, Siginfo *info, void *context) -{ - G *gp; - M *mp; -#ifdef USING_SPLIT_STACK - void *stack_context[10]; -#endif - - /* We are now running on the stack registered via sigaltstack. - (Actually there is a small span of time between runtime_siginit - and sigaltstack when the program starts.) */ - gp = runtime_g (); - mp = runtime_m (); - - if (gp != NULL) - { -#ifdef USING_SPLIT_STACK - __splitstack_getcontext (&stack_context[0]); -#endif - } - - if (gp != NULL && mp->gsignal != NULL) - { - /* We are running on the signal stack. Set the split stack - context so that the stack guards are checked correctly. */ -#ifdef USING_SPLIT_STACK - __splitstack_setcontext (&mp->gsignal->stackcontext[0]); -#endif - } - - runtime_sighandler (sig, info, context, gp); - - /* We are going to return back to the signal trampoline and then to - whatever we were doing before we got the signal. Restore the - split stack context so that stack guards are checked - correctly. */ - - if (gp != NULL) - { -#ifdef USING_SPLIT_STACK - __splitstack_setcontext (&stack_context[0]); -#endif - } -} - -#ifndef SA_SIGINFO - -static void sig_tramp (int sig) __attribute__ ((no_split_stack)); - -static void -sig_tramp (int sig) -{ - sig_tramp_info (sig, NULL, NULL); -} - -#endif - -void -runtime_setsig (int32 i, GoSighandler *fn, bool restart) -{ - struct sigaction sa; - int r; - SigTab *t; - - memset (&sa, 0, sizeof sa); - - r = sigfillset (&sa.sa_mask); - __go_assert (r == 0); - - t = &runtime_sigtab[i]; - - if ((t->flags & _SigPanic) == 0) - { -#ifdef SA_SIGINFO - sa.sa_flags = SA_ONSTACK | SA_SIGINFO; - if (fn == runtime_sighandler) - fn = (void *) sig_tramp_info; - sa.sa_sigaction = (void *) fn; -#else - sa.sa_flags = SA_ONSTACK; - if (fn == runtime_sighandler) - fn = (void *) sig_tramp; - sa.sa_handler = (void *) fn; -#endif - } - else - { -#ifdef SA_SIGINFO - sa.sa_flags = SA_SIGINFO; - if (fn == runtime_sighandler) - fn = (void *) sig_panic_info_handler; - sa.sa_sigaction = (void *) fn; -#else - sa.sa_flags = 0; - if (fn == runtime_sighandler) - fn = (void *) sig_panic_handler; - sa.sa_handler = (void *) fn; -#endif - } - - if (restart) - sa.sa_flags |= SA_RESTART; - - if (sigaction (t->sig, &sa, NULL) != 0) - __go_assert (0); -} - -GoSighandler* -runtime_getsig (int32 i) -{ - struct sigaction sa; - int r; - SigTab *t; - - memset (&sa, 0, sizeof sa); - - r = sigemptyset (&sa.sa_mask); - __go_assert (r == 0); - - t = &runtime_sigtab[i]; - - if (sigaction (t->sig, NULL, &sa) != 0) - runtime_throw ("sigaction read failure"); - - if ((void *) sa.sa_handler == sig_tramp_info) - return runtime_sighandler; -#ifdef SA_SIGINFO - if ((void *) sa.sa_handler == sig_panic_info_handler) - return runtime_sighandler; -#else - if ((void *) sa.sa_handler == sig_tramp - || (void *) sa.sa_handler == sig_panic_handler) - return runtime_sighandler; -#endif - - return (void *) sa.sa_handler; -} - -/* Used by the os package to raise SIGPIPE. */ - -void os_sigpipe (void) __asm__ (GOSYM_PREFIX "os.sigpipe"); - -void -os_sigpipe (void) -{ - struct sigaction sa; - int i; - - if (__go_sigsend (SIGPIPE)) - return; - - memset (&sa, 0, sizeof sa); - - sa.sa_handler = SIG_DFL; - - i = sigemptyset (&sa.sa_mask); - __go_assert (i == 0); - - if (sigaction (SIGPIPE, &sa, NULL) != 0) - abort (); - - raise (SIGPIPE); -} - -void -runtime_setprof(bool on) -{ - USED(on); + runtime_printf("eax %X\n", m->gregs[REG_EAX]); + runtime_printf("ebx %X\n", m->gregs[REG_EBX]); + runtime_printf("ecx %X\n", m->gregs[REG_ECX]); + runtime_printf("edx %X\n", m->gregs[REG_EDX]); + runtime_printf("edi %X\n", m->gregs[REG_EDI]); + runtime_printf("esi %X\n", m->gregs[REG_ESI]); + runtime_printf("ebp %X\n", m->gregs[REG_EBP]); + runtime_printf("esp %X\n", m->gregs[REG_ESP]); + runtime_printf("eip %X\n", m->gregs[REG_EIP]); + runtime_printf("eflags %X\n", m->gregs[REG_EFL]); + runtime_printf("cs %X\n", m->gregs[REG_CS]); + runtime_printf("fs %X\n", m->gregs[REG_FS]); + runtime_printf("gs %X\n", m->gregs[REG_GS]); + } + #endif +#endif } diff --git a/libgo/runtime/panic.c b/libgo/runtime/panic.c index 0dd677693af..78c8f5d4b5b 100644 --- a/libgo/runtime/panic.c +++ b/libgo/runtime/panic.c @@ -122,9 +122,13 @@ runtime_dopanic(int32 unused __attribute__ ((unused))) int32 t; g = runtime_g(); - if(g->sig != 0) - runtime_printf("[signal %x code=%p addr=%p]\n", + if(g->sig != 0) { + runtime_printf("[signal %x code=%p addr=%p", g->sig, (void*)g->sigcode0, (void*)g->sigcode1); + if (g->sigpc != 0) + runtime_printf(" pc=%p", g->sigpc); + runtime_printf("]\n"); + } if((t = runtime_gotraceback(&crash)) > 0){ if(g != runtime_m()->g0) { diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index a9f3b837334..62abc9d238b 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -2022,7 +2022,8 @@ goexit0(G *gp) // entersyscall is going to return immediately after. void runtime_entersyscall(int32) __attribute__ ((no_split_stack)); -static void doentersyscall(void) __attribute__ ((no_split_stack, noinline)); +static void doentersyscall(uintptr, uintptr) + __attribute__ ((no_split_stack, noinline)); void runtime_entersyscall(int32 dummy __attribute__ ((unused))) @@ -2040,11 +2041,12 @@ runtime_entersyscall(int32 dummy __attribute__ ((unused))) // callee-saved registers to access the TLS variable g. We // don't want to put the ucontext_t on the stack because it is // large and we can not split the stack here. - doentersyscall(); + doentersyscall((uintptr)runtime_getcallerpc(&dummy), + (uintptr)runtime_getcallersp(&dummy)); } static void -doentersyscall() +doentersyscall(uintptr pc, uintptr sp) { // Disable preemption because during this function g is in _Gsyscall status, // but can have inconsistent g->sched, do not let GC observe it. @@ -2067,6 +2069,9 @@ doentersyscall() } #endif + g->syscallsp = sp; + g->syscallpc = pc; + g->atomicstatus = _Gsyscall; if(runtime_atomicload(&runtime_sched.sysmonwait)) { // TODO: fast atomic @@ -2118,6 +2123,9 @@ runtime_entersyscallblock(int32 dummy __attribute__ ((unused))) // held in registers will be seen by the garbage collector. getcontext(ucontext_arg(&g->gcregs[0])); + g->syscallpc = (uintptr)runtime_getcallerpc(&dummy); + g->syscallsp = (uintptr)runtime_getcallersp(&dummy); + g->atomicstatus = _Gsyscall; p = releasep(); @@ -2155,6 +2163,7 @@ runtime_exitsyscall(int32 dummy __attribute__ ((unused))) #endif gp->gcnextsp = nil; runtime_memclr(&gp->gcregs[0], sizeof gp->gcregs); + gp->syscallsp = 0; gp->m->locks--; return; } @@ -2176,6 +2185,8 @@ runtime_exitsyscall(int32 dummy __attribute__ ((unused))) gp->gcnextsp = nil; runtime_memclr(&gp->gcregs[0], sizeof gp->gcregs); + gp->syscallsp = 0; + // Note that this gp->m might be different than the earlier // gp->m after returning from runtime_mcall. ((P*)gp->m->p)->syscalltick++; diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 501f1b41ace..7d22631bddc 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -282,10 +282,8 @@ void* runtime_mal(uintptr); String runtime_gostringnocopy(const byte*) __asm__ (GOSYM_PREFIX "runtime.gostringnocopy"); void runtime_schedinit(void); -void runtime_initsig(bool); -void runtime_sigenable(uint32 sig); -void runtime_sigdisable(uint32 sig); -void runtime_sigignore(uint32 sig); +void runtime_initsig(bool) + __asm__ (GOSYM_PREFIX "runtime.initsig"); int32 runtime_gotraceback(bool *crash); void runtime_goroutineheader(G*) __asm__ (GOSYM_PREFIX "runtime.goroutineheader"); @@ -303,8 +301,10 @@ G* runtime_malg(int32, byte**, uintptr*); void runtime_mpreinit(M*); void runtime_minit(void); void runtime_unminit(void); -void runtime_needm(void); -void runtime_dropm(void); +void runtime_needm(void) + __asm__ (GOSYM_PREFIX "runtime.needm"); +void runtime_dropm(void) + __asm__ (GOSYM_PREFIX "runtime.dropm"); void runtime_signalstack(byte*, int32); MCache* runtime_allocmcache(void); void runtime_freemcache(MCache*); @@ -312,8 +312,9 @@ void runtime_mallocinit(void); void runtime_mprofinit(void); #define runtime_malloc(s) __go_alloc(s) #define runtime_free(p) __go_free(p) -#define runtime_getcallersp(p) __builtin_frame_address(1) -int32 runtime_mcount(void); +#define runtime_getcallersp(p) __builtin_frame_address(0) +int32 runtime_mcount(void) + __asm__ (GOSYM_PREFIX "runtime.mcount"); int32 runtime_gcount(void); void runtime_mcall(void(*)(G*)); uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1"); @@ -339,7 +340,8 @@ int32 runtime_round2(int32 x); // round x up to a power of 2. #define runtime_atomicloadp(p) __atomic_load_n (p, __ATOMIC_SEQ_CST) #define runtime_atomicstorep(p, v) __atomic_store_n (p, v, __ATOMIC_SEQ_CST) -void runtime_setg(G*); +void runtime_setg(G*) + __asm__ (GOSYM_PREFIX "runtime.setg"); void runtime_newextram(void); #define runtime_exit(s) exit(s) #define runtime_breakpoint() __builtin_trap() @@ -358,19 +360,20 @@ void runtime_entersyscallblock(int32) void runtime_exitsyscall(int32) __asm__ (GOSYM_PREFIX "runtime.exitsyscall"); G* __go_go(void (*pfn)(void*), void*); -void siginit(void); -bool __go_sigsend(int32 sig); int32 runtime_callers(int32, Location*, int32, bool keep_callers); int64 runtime_nanotime(void) // monotonic time __asm__(GOSYM_PREFIX "runtime.nanotime"); int64 runtime_unixnanotime(void) // real time, can skip __asm__ (GOSYM_PREFIX "runtime.unixnanotime"); void runtime_dopanic(int32) __attribute__ ((noreturn)); -void runtime_startpanic(void); +void runtime_startpanic(void) + __asm__ (GOSYM_PREFIX "runtime.startpanic"); void runtime_freezetheworld(void); void runtime_unwindstack(G*, byte*); -void runtime_sigprof(); -void runtime_resetcpuprofiler(int32); +void runtime_sigprof() + __asm__ (GOSYM_PREFIX "runtime.sigprof"); +void runtime_resetcpuprofiler(int32) + __asm__ (GOSYM_PREFIX "runtime.resetcpuprofiler"); void runtime_setcpuprofilerate_m(int32) __asm__ (GOSYM_PREFIX "runtime.setcpuprofilerate_m"); void runtime_cpuprofAdd(Slice) @@ -385,7 +388,8 @@ void runtime_blockevent(int64, int32); extern int64 runtime_blockprofilerate; G* runtime_netpoll(bool) __asm__ (GOSYM_PREFIX "runtime.netpoll"); -void runtime_crash(void); +void runtime_crash(void) + __asm__ (GOSYM_PREFIX "runtime.crash"); void runtime_parsedebugvars(void) __asm__(GOSYM_PREFIX "runtime.parsedebugvars"); void _rt0_go(void); diff --git a/libgo/runtime/signal_unix.c b/libgo/runtime/signal_unix.c deleted file mode 100644 index 2cca08cf47b..00000000000 --- a/libgo/runtime/signal_unix.c +++ /dev/null @@ -1,176 +0,0 @@ -// 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 darwin dragonfly freebsd linux netbsd openbsd solaris - -#include - -#include "runtime.h" -#include "defs.h" -#include "signal_unix.h" - -extern SigTab runtime_sigtab[]; - -void -runtime_initsig(bool preinit) -{ - int32 i; - SigTab *t; - - // For c-archive/c-shared this is called by go-libmain.c with - // preinit == true. - if(runtime_isarchive && !preinit) - return; - - // First call: basic setup. - for(i = 0; runtime_sigtab[i].sig != -1; i++) { - t = &runtime_sigtab[i]; - if((t->flags == 0) || (t->flags & _SigDefault)) - continue; - - t->fwdsig = runtime_getsig(i); - - // 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(t->sig) { - case SIGHUP: - case SIGINT: - if(t->fwdsig == GO_SIG_IGN) { - continue; - } - } - - if(runtime_isarchive && (t->flags&_SigPanic) == 0) - continue; - - t->flags |= _SigHandling; - runtime_setsig(i, runtime_sighandler, true); - } -} - -void -runtime_sigenable(uint32 sig) -{ - int32 i; - SigTab *t; - - t = nil; - for(i = 0; runtime_sigtab[i].sig != -1; i++) { - if(runtime_sigtab[i].sig == (int32)sig) { - t = &runtime_sigtab[i]; - break; - } - } - - if(t == nil) - return; - - if((t->flags & _SigNotify) && !(t->flags & _SigHandling)) { - t->flags |= _SigHandling; - t->fwdsig = runtime_getsig(i); - runtime_setsig(i, runtime_sighandler, true); - } -} - -void -runtime_sigdisable(uint32 sig) -{ - int32 i; - SigTab *t; - - t = nil; - for(i = 0; runtime_sigtab[i].sig != -1; i++) { - if(runtime_sigtab[i].sig == (int32)sig) { - t = &runtime_sigtab[i]; - break; - } - } - - if(t == nil) - return; - - if((sig == SIGHUP || sig == SIGINT) && t->fwdsig == GO_SIG_IGN) { - t->flags &= ~_SigHandling; - runtime_setsig(i, t->fwdsig, true); - } -} - -void -runtime_sigignore(uint32 sig) -{ - int32 i; - SigTab *t; - - t = nil; - for(i = 0; runtime_sigtab[i].sig != -1; i++) { - if(runtime_sigtab[i].sig == (int32)sig) { - t = &runtime_sigtab[i]; - break; - } - } - - if(t == nil) - return; - - if((t->flags & _SigNotify) != 0) { - t->flags &= ~_SigHandling; - runtime_setsig(i, GO_SIG_IGN, true); - } -} - -void -runtime_resetcpuprofiler(int32 hz) -{ - struct itimerval it; - - runtime_memclr((byte*)&it, sizeof it); - if(hz == 0) { - runtime_setitimer(ITIMER_PROF, &it, nil); - } else { - it.it_interval.tv_sec = 0; - it.it_interval.tv_usec = 1000000 / hz; - it.it_value = it.it_interval; - runtime_setitimer(ITIMER_PROF, &it, nil); - } - runtime_m()->profilehz = hz; -} - -void -runtime_unblocksignals(void) -{ - sigset_t sigset_none; - sigemptyset(&sigset_none); - pthread_sigmask(SIG_SETMASK, &sigset_none, nil); -} - -void -runtime_crash(void) -{ - int32 i; - -#ifdef 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(sizeof(void*) == 8) - return; -#endif - - runtime_unblocksignals(); - for(i = 0; runtime_sigtab[i].sig != -1; i++) - if(runtime_sigtab[i].sig == SIGABRT) - break; - runtime_setsig(i, GO_SIG_DFL, false); - runtime_raise(SIGABRT); -} - -void -runtime_raise(int32 sig) -{ - raise(sig); -} diff --git a/libgo/runtime/signal_unix.h b/libgo/runtime/signal_unix.h deleted file mode 100644 index 1c51740bf1d..00000000000 --- a/libgo/runtime/signal_unix.h +++ /dev/null @@ -1,22 +0,0 @@ -// 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. - -#include - -#define GO_SIG_DFL ((void*)SIG_DFL) -#define GO_SIG_IGN ((void*)SIG_IGN) - -#ifdef SA_SIGINFO -typedef siginfo_t Siginfo; -#else -typedef void *Siginfo; -#endif - -typedef void GoSighandler(int32, Siginfo*, void*, G*); -void runtime_setsig(int32, GoSighandler*, bool); -GoSighandler* runtime_getsig(int32); - -void runtime_sighandler(int32 sig, Siginfo *info, void *context, G *gp); -void runtime_raise(int32); - diff --git a/libgo/runtime/sigqueue.goc b/libgo/runtime/sigqueue.goc deleted file mode 100644 index fba1c71e2cd..00000000000 --- a/libgo/runtime/sigqueue.goc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2009 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. - -// This file implements runtime support for signal handling. -// -// Most synchronization primitives are not available from -// the signal handler (it cannot block, allocate memory, or use locks) -// so the handler communicates with a processing goroutine -// via struct sig, below. -// -// sigsend() is called by the signal handler to queue a new signal. -// signal_recv() is called by the Go program to receive a newly queued signal. -// Synchronization between sigsend() and signal_recv() is based on the sig.state -// variable. It can be in 3 states: 0, HASWAITER and HASSIGNAL. -// HASWAITER means that signal_recv() is blocked on sig.Note and there are no -// new pending signals. -// HASSIGNAL means that sig.mask *may* contain new pending signals, -// signal_recv() can't be blocked in this state. -// 0 means that there are no new pending signals and signal_recv() is not blocked. -// Transitions between states are done atomically with CAS. -// When signal_recv() is unblocked, it resets sig.Note and rechecks sig.mask. -// If several sigsend()'s and signal_recv() execute concurrently, it can lead to -// unnecessary rechecks of sig.mask, but must not lead to missed signals -// nor deadlocks. - -package signal -#include "config.h" -#include "runtime.h" -#include "arch.h" -#include "malloc.h" -#include "defs.h" - -static struct { - Note; - uint32 mask[(NSIG+31)/32]; - uint32 wanted[(NSIG+31)/32]; - uint32 state; - bool inuse; -} sig; - -enum { - HASWAITER = 1, - HASSIGNAL = 2, -}; - -// Called from sighandler to send a signal back out of the signal handling thread. -bool -__go_sigsend(int32 s) -{ - uint32 bit, mask, old, new; - - if(!sig.inuse || s < 0 || (size_t)s >= 32*nelem(sig.wanted) || !(sig.wanted[s/32]&(1U<<(s&31)))) - return false; - bit = 1 << (s&31); - for(;;) { - mask = sig.mask[s/32]; - if(mask & bit) - break; // signal already in queue - if(runtime_cas(&sig.mask[s/32], mask, mask|bit)) { - // Added to queue. - // Only send a wakeup if the receiver needs a kick. - for(;;) { - old = runtime_atomicload(&sig.state); - if(old == HASSIGNAL) - break; - if(old == HASWAITER) - new = 0; - else // if(old == 0) - new = HASSIGNAL; - if(runtime_cas(&sig.state, old, new)) { - if (old == HASWAITER) - runtime_notewakeup(&sig); - break; - } - } - break; - } - } - return true; -} - -// Called to receive the next queued signal. -// Must only be called from a single goroutine at a time. -func signal_recv() (m uint32) { - static uint32 recv[nelem(sig.mask)]; - uint32 i, old, new; - - for(;;) { - // Serve from local copy if there are bits left. - for(i=0; i= nelem(sig.wanted)*32) - return; - sig.wanted[s/32] |= 1U<<(s&31); - runtime_sigenable(s); -} - -// Must only be called from a single goroutine at a time. -func signal_disable(s uint32) { - if(s >= nelem(sig.wanted)*32) - return; - sig.wanted[s/32] &= ~(1U<<(s&31)); - runtime_sigdisable(s); -} - -// Must only be called from a single goroutine at a time. -func signal_ignore(s uint32) { - if (s >= nelem(sig.wanted)*32) - return; - sig.wanted[s/32] &= ~(1U<<(s&31)); - runtime_sigignore(s); -} - -// This runs on a foreign stack, without an m or a g. No stack split. -void -runtime_badsignal(int sig) -{ - __go_sigsend(sig); -} diff --git a/libgo/runtime/thread-linux.c b/libgo/runtime/thread-linux.c index 63a2b7551f6..81ad0f9c902 100644 --- a/libgo/runtime/thread-linux.c +++ b/libgo/runtime/thread-linux.c @@ -4,7 +4,6 @@ #include "runtime.h" #include "defs.h" -#include "signal_unix.h" // Linux futex. diff --git a/libgo/runtime/yield.c b/libgo/runtime/yield.c index 442d346db7d..d0aed8d22ed 100644 --- a/libgo/runtime/yield.c +++ b/libgo/runtime/yield.c @@ -37,6 +37,9 @@ runtime_procyield (uint32 cnt) /* Ask the OS to reschedule this thread. */ +void runtime_osyield(void) + __attribute__ ((no_split_stack)); + void runtime_osyield (void) {