]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't install a stack barrier in cgocallback_gofunc's frame
authorAustin Clements <austin@google.com>
Wed, 26 Aug 2015 16:16:51 +0000 (12:16 -0400)
committerAustin Clements <austin@google.com>
Sun, 30 Aug 2015 16:06:47 +0000 (16:06 +0000)
Currently the runtime can install stack barriers in any frame.
However, the frame of cgocallback_gofunc is special: it's the one
function that switches from a regular G stack to the system stack on
return. Hence, the return PC slot in its frame on the G stack is
actually used to save getg().sched.pc (so tracebacks appear to unwind
to the last Go function running on that G), and not as an actual
return PC for cgocallback_gofunc.

Because of this, if we install a stack barrier in cgocallback_gofunc's
return PC slot, when cgocallback_gofunc does return, it will move the
stack barrier stub PC in to getg().sched.pc and switch back to the
system stack. The rest of the runtime doesn't know how to deal with a
stack barrier stub in sched.pc: nothing knows how to match it up with
the G's stack barrier array and, when the runtime removes stack
barriers, it doesn't know to undo the one in sched.pc. Hence, if the C
code later returns back in to Go code, it will attempt to return
through the stack barrier saved in sched.pc, which may no longer have
correct unwinding information.

Fix this by blacklisting cgocallback_gofunc's frame so the runtime
won't install a stack barrier in it's return PC slot.

Fixes #12238.

Change-Id: I46aa2155df2fd050dd50de3434b62987dc4947b8
Reviewed-on: https://go-review.googlesource.com/13944
Reviewed-by: Russ Cox <rsc@golang.org>
src/runtime/asm_amd64p32.s
src/runtime/mgcmark.go
src/runtime/traceback.go

index e393431f2654829e67e8f1bccd054cbe4fd8dc4d..e8b14912b9a8dc4d9421beb8a4607802ccdbb238 100644 (file)
@@ -592,6 +592,12 @@ TEXT runtime·cgocallback(SB),NOSPLIT,$0-12
        MOVL    0, AX
        RET
 
+// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize)
+// Not implemented.
+TEXT ·cgocallback_gofunc(SB),NOSPLIT,$0-12
+       MOVL    0, AX
+       RET
+
 // void setg(G*); set g. for use by needm.
 // Not implemented.
 TEXT runtime·setg(SB), NOSPLIT, $0-4
index 42aacb63a619b146f2b0b89112387f1d81d3b40e..650d03862e84ccf8855962f0942afd2e27bc75c2 100644 (file)
@@ -388,9 +388,10 @@ func scanstack(gp *g) {
                        // frame because on LR machines this LR is not
                        // on the stack.
                        if gcphase == _GCscan && n != 0 {
-                               gcInstallStackBarrier(gp, frame)
-                               barrierOffset *= 2
-                               nextBarrier = sp + barrierOffset
+                               if gcInstallStackBarrier(gp, frame) {
+                                       barrierOffset *= 2
+                                       nextBarrier = sp + barrierOffset
+                               }
                        } else if gcphase == _GCmarktermination {
                                // We just scanned a frame containing
                                // a return to a stack barrier. Since
@@ -509,12 +510,25 @@ func gcMaxStackBarriers(stackSize int) (n int) {
 
 // gcInstallStackBarrier installs a stack barrier over the return PC of frame.
 //go:nowritebarrier
-func gcInstallStackBarrier(gp *g, frame *stkframe) {
+func gcInstallStackBarrier(gp *g, frame *stkframe) bool {
        if frame.lr == 0 {
                if debugStackBarrier {
                        print("not installing stack barrier with no LR, goid=", gp.goid, "\n")
                }
-               return
+               return false
+       }
+
+       if frame.fn.entry == cgocallback_gofuncPC {
+               // cgocallback_gofunc doesn't return to its LR;
+               // instead, its return path puts LR in g.sched.pc and
+               // switches back to the system stack on which
+               // cgocallback_gofunc was originally called. We can't
+               // have a stack barrier in g.sched.pc, so don't
+               // install one in this frame.
+               if debugStackBarrier {
+                       print("not installing stack barrier over LR of cgocallback_gofunc, goid=", gp.goid, "\n")
+               }
+               return false
        }
 
        // Save the return PC and overwrite it with stackBarrier.
@@ -538,6 +552,7 @@ func gcInstallStackBarrier(gp *g, frame *stkframe) {
        stkbar.savedLRPtr = lrUintptr
        stkbar.savedLRVal = uintptr(*lrPtr)
        *lrPtr = uintreg(stackBarrierPC)
+       return true
 }
 
 // gcRemoveStackBarriers removes all stack barriers installed in gp's stack.
index 1025032aeecded0c2b2681f86004fdb664faa0a3..544ce273ee28a0e8be6874508564ced75446d596 100644 (file)
@@ -48,6 +48,7 @@ var (
        systemstack_switchPC uintptr
        systemstackPC        uintptr
        stackBarrierPC       uintptr
+       cgocallback_gofuncPC uintptr
 
        gogoPC uintptr
 
@@ -75,6 +76,7 @@ func tracebackinit() {
        systemstack_switchPC = funcPC(systemstack_switch)
        systemstackPC = funcPC(systemstack)
        stackBarrierPC = funcPC(stackBarrier)
+       cgocallback_gofuncPC = funcPC(cgocallback_gofunc)
 
        // used by sigprof handler
        gogoPC = funcPC(gogo)