Skip to content

Commit 7e97d39

Browse files
committed
cmd/5g, cmd/6g, cmd/8g: fix line number of caller of deferred func
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
1 parent 031c107 commit 7e97d39

File tree

4 files changed

+78
-0
lines changed

4 files changed

+78
-0
lines changed

src/cmd/5g/ggen.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ ginscall(Node *f, int proc)
8484
case 0: // normal call
8585
case -1: // normal call but no return
8686
if(f->op == ONAME && f->class == PFUNC) {
87+
if(f == deferreturn) {
88+
// Deferred calls will appear to be returning to
89+
// the BL deferreturn(SB) that we are about to emit.
90+
// However, the stack trace code will show the line
91+
// of the instruction before that return PC.
92+
// To avoid that instruction being an unrelated instruction,
93+
// insert a NOP so that we will have the right line number.
94+
// ARM NOP 0x00000000 is really AND.EQ R0, R0, R0.
95+
// Use the latter form because the NOP pseudo-instruction
96+
// would be removed by the linker.
97+
nodreg(&r, types[TINT], 0);
98+
p = gins(AAND, &r, &r);
99+
p->scond = C_SCOND_EQ;
100+
}
87101
p = gins(ABL, N, f);
88102
afunclit(&p->to, f);
89103
if(proc == -1 || noreturn(p))

src/cmd/6g/ggen.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ ginscall(Node *f, int proc)
8282
case 0: // normal call
8383
case -1: // normal call but no return
8484
if(f->op == ONAME && f->class == PFUNC) {
85+
if(f == deferreturn) {
86+
// Deferred calls will appear to be returning to
87+
// the CALL deferreturn(SB) that we are about to emit.
88+
// However, the stack trace code will show the line
89+
// of the instruction byte before the return PC.
90+
// To avoid that being an unrelated instruction,
91+
// insert an x86 NOP that we will have the right line number.
92+
// x86 NOP 0x90 is really XCHG AX, AX; use that description
93+
// because the NOP pseudo-instruction would be removed by
94+
// the linker.
95+
nodreg(&reg, types[TINT], D_AX);
96+
gins(AXCHGL, &reg, &reg);
97+
}
8598
p = gins(ACALL, N, f);
8699
afunclit(&p->to, f);
87100
if(proc == -1 || noreturn(p))

src/cmd/8g/ggen.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ ginscall(Node *f, int proc)
126126
case 0: // normal call
127127
case -1: // normal call but no return
128128
if(f->op == ONAME && f->class == PFUNC) {
129+
if(f == deferreturn) {
130+
// Deferred calls will appear to be returning to
131+
// the CALL deferreturn(SB) that we are about to emit.
132+
// However, the stack trace code will show the line
133+
// of the instruction byte before the return PC.
134+
// To avoid that being an unrelated instruction,
135+
// insert an x86 NOP that we will have the right line number.
136+
// x86 NOP 0x90 is really XCHG AX, AX; use that description
137+
// because the NOP pseudo-instruction will be removed by
138+
// the linker.
139+
nodreg(&reg, types[TINT], D_AX);
140+
gins(AXCHGL, &reg, &reg);
141+
}
129142
p = gins(ACALL, N, f);
130143
afunclit(&p->to, f);
131144
if(proc == -1 || noreturn(p))

test/fixedbugs/issue5856.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run
2+
3+
// Copyright 2013 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"runtime"
13+
"strings"
14+
)
15+
16+
func main() {
17+
f()
18+
panic("deferred function not run")
19+
}
20+
21+
var x = 1
22+
23+
func f() {
24+
if x == 0 {
25+
return
26+
}
27+
defer g()
28+
panic("panic")
29+
}
30+
31+
func g() {
32+
_, file, line, _ := runtime.Caller(2)
33+
if !strings.HasSuffix(file, "issue5856.go") || line != 28 {
34+
fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line)
35+
os.Exit(1)
36+
}
37+
os.Exit(0)
38+
}

0 commit comments

Comments
 (0)