The gc library allocates a _panic struct on the stack. This does not
work for gccgo, because when a deferred function recovers the panic we
unwind the stack up to that point so that returning from the function
will work correctly.

Allocating on the stack fine if the panic is not recovered, and it
works fine if the panic is recovered by a function that returns.
However, it fails if the panic is recovered by a function that itself
panics, and if that second panic is then recovered by a function
higher up on the stack. When we unwind the stack to that second panic,
the g will wind up pointing at a panic farther down on the stack. Even
then everything will often work fine, except when the deferred
function catching the second panic makes a bunch of calls that use
stack space before returning. In that case the code can overwrite the
panic struct, which will then cause disaster when we remove the struct
from the linked list, as the link field will be garbage. This case is
rare enough that all the x86 tests were passing, but there was a
failure on ppc64le.

Before https://golang.org/cl/33414 we allocated the panic struct on
the heap, so go back to doing that again.

This fixes https://golang.org/issue18228.

Patch bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.
Verified that failing test now passes on ppc64le-linux-gnu.  Committed
to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 243442)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-7a941ba323660ec7034cd92d4eab466024a3c72c
+2442fca7be8a4f51ddc91070fa69ef66e24593ac
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/go/runtime/panic.go
===================================================================
--- libgo/go/runtime/panic.go   (revision 243084)
+++ libgo/go/runtime/panic.go   (working copy)
@@ -415,10 +415,19 @@ func gopanic(e interface{}) {
                throw("panic holding locks")
        }
 
-       var p _panic
-       p.arg = e
-       p.link = gp._panic
-       gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
+       // The gc compiler allocates this new _panic struct on the
+       // stack. We can't do that, because when a deferred function
+       // recovers the panic we unwind the stack. We unlink this
+       // entry before unwinding the stack, but that doesn't help in
+       // the case where we panic, a deferred function recovers and
+       // then panics itself, that panic is in turn recovered, and
+       // unwinds the stack past this stack frame.
+
+       p := &_panic{
+               arg:  e,
+               link: gp._panic,
+       }
+       gp._panic = p
 
        for {
                d := gp._defer

Reply via email to