aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2015-08-26 12:16:51 -0400
committerAndrew Gerrand <adg@golang.org>2015-09-08 06:04:48 +0000
commit23ef1e1933f4483572159277a99f2efc5003e1a2 (patch)
treeec5b5ce024f8d827d20c3f18e3e21ef29a1d1779
parentf7d740377612142c6a08e460b5c542ebe0d758b3 (diff)
downloadgo-23ef1e1933f4483572159277a99f2efc5003e1a2.tar.gz
go-23ef1e1933f4483572159277a99f2efc5003e1a2.zip
[release-branch.go1.5] runtime: don't install a stack barrier in cgocallback_gofunc's frame
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> Reviewed-on: https://go-review.googlesource.com/14229 Reviewed-by: Austin Clements <austin@google.com>
-rw-r--r--src/runtime/asm_amd64p32.s6
-rw-r--r--src/runtime/mgcmark.go25
-rw-r--r--src/runtime/traceback.go2
3 files changed, 28 insertions, 5 deletions
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index a5d6e8155a..6e97256c70 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -599,6 +599,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
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 2e438307ea..44f951269b 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -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.
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 48ef6e5e27..2def3592d6 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -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)