]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/5g, cmd/6g, cmd/8g: fix line number of caller of deferred func
authorRuss Cox <rsc@golang.org>
Fri, 12 Jul 2013 17:47:55 +0000 (13:47 -0400)
committerRuss Cox <rsc@golang.org>
Fri, 12 Jul 2013 17:47:55 +0000 (13:47 -0400)
Deferred functions are not run by a call instruction. They are run by
the runtime editing registers to make the call start with a caller PC
returning to a
        CALL deferreturn
instruction.

That instruction has always had the line number of the function's
closing brace, but that instruction's line number is irrelevant.
Stack traces show the line number of the instruction before the
return PC, because normally that's what started the call. Not so here.
The instruction before the CALL deferreturn could be almost anywhere
in the function; it's unrelated and its line number is incorrect to show.

Fix the line number by inserting a true hardware no-op with the right
line number before the returned-to CALL instruction. That is, the deferred
calls now appear to start with a caller PC returning to the second instruction
in this sequence:
        NOP
        CALL deferreturn

The traceback will show the line number of the NOP, which we've set
to be the line number of the function's closing brace.

The NOP here is not the usual pseudo-instruction, which would be
elided by the linker. Instead it is the real hardware instruction:
XCHG AX, AX on 386 and amd64, and AND.EQ R0, R0, R0 on ARM.

Fixes #5856.

R=ken2, ken
CC=golang-dev
https://golang.org/cl/11223043

src/cmd/5g/ggen.c
src/cmd/6g/ggen.c
src/cmd/8g/ggen.c
test/fixedbugs/issue5856.go [new file with mode: 0644]

index 70049a89cc1c7c3b13553122a2a23c4c49cf2a65..eb027c6a67ddcd3890fd5af5765b72527cef48f9 100644 (file)
@@ -84,6 +84,20 @@ ginscall(Node *f, int proc)
        case 0: // normal call
        case -1:        // normal call but no return
                if(f->op == ONAME && f->class == PFUNC) {
+                       if(f == deferreturn) {
+                               // Deferred calls will appear to be returning to
+                               // the BL deferreturn(SB) that we are about to emit.
+                               // However, the stack trace code will show the line
+                               // of the instruction before that return PC. 
+                               // To avoid that instruction being an unrelated instruction,
+                               // insert a NOP so that we will have the right line number.
+                               // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0.
+                               // Use the latter form because the NOP pseudo-instruction
+                               // would be removed by the linker.
+                               nodreg(&r, types[TINT], 0);
+                               p = gins(AAND, &r, &r);
+                               p->scond = C_SCOND_EQ;
+                       }
                        p = gins(ABL, N, f);
                        afunclit(&p->to, f);
                        if(proc == -1 || noreturn(p))
index a47de23bdbaef1f0fb9df69b68a0d536d8d6336c..36d9dce4669b2923edd45d30369bc8d956293998 100644 (file)
@@ -82,6 +82,19 @@ ginscall(Node *f, int proc)
        case 0: // normal call
        case -1:        // normal call but no return
                if(f->op == ONAME && f->class == PFUNC) {
+                       if(f == deferreturn) {
+                               // Deferred calls will appear to be returning to
+                               // the CALL deferreturn(SB) that we are about to emit.
+                               // However, the stack trace code will show the line
+                               // of the instruction byte before the return PC. 
+                               // To avoid that being an unrelated instruction,
+                               // insert an x86 NOP that we will have the right line number.
+                               // x86 NOP 0x90 is really XCHG AX, AX; use that description
+                               // because the NOP pseudo-instruction would be removed by
+                               // the linker.
+                               nodreg(&reg, types[TINT], D_AX);
+                               gins(AXCHGL, &reg, &reg);
+                       }
                        p = gins(ACALL, N, f);
                        afunclit(&p->to, f);
                        if(proc == -1 || noreturn(p))
index 60b22bbea2df440c6f0446facea18c641b3127da..4dec3c808268f562ade76f11b502347de47f9753 100644 (file)
@@ -126,6 +126,19 @@ ginscall(Node *f, int proc)
        case 0: // normal call
        case -1:        // normal call but no return
                if(f->op == ONAME && f->class == PFUNC) {
+                       if(f == deferreturn) {
+                               // Deferred calls will appear to be returning to
+                               // the CALL deferreturn(SB) that we are about to emit.
+                               // However, the stack trace code will show the line
+                               // of the instruction byte before the return PC. 
+                               // To avoid that being an unrelated instruction,
+                               // insert an x86 NOP that we will have the right line number.
+                               // x86 NOP 0x90 is really XCHG AX, AX; use that description
+                               // because the NOP pseudo-instruction will be removed by
+                               // the linker.
+                               nodreg(&reg, types[TINT], D_AX);
+                               gins(AXCHGL, &reg, &reg);
+                       }
                        p = gins(ACALL, N, f);
                        afunclit(&p->to, f);
                        if(proc == -1 || noreturn(p))
diff --git a/test/fixedbugs/issue5856.go b/test/fixedbugs/issue5856.go
new file mode 100644 (file)
index 0000000..35cadf8
--- /dev/null
@@ -0,0 +1,38 @@
+// run
+
+// 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.
+
+package main
+
+import (
+       "fmt"
+       "os"
+       "runtime"
+       "strings"
+)
+
+func main() {
+       f()
+       panic("deferred function not run")
+}
+
+var x = 1
+
+func f() {
+       if x == 0 {
+               return
+       }
+       defer g()
+       panic("panic")
+}
+
+func g() {
+       _, file, line, _ := runtime.Caller(2)
+       if !strings.HasSuffix(file, "issue5856.go") || line != 28 {
+               fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line)
+               os.Exit(1)
+       }
+       os.Exit(0)
+}