Skip to content

Commit 2b70ffe

Browse files
committed
cmd/link: detect trampoline of deferreturn call
The runtime needs to find the PC of the deferreturn call in a few places. So for functions that have defer, we record the PC of deferreturn call in its funcdata. For very large binaries, the deferreturn call could be made through a trampoline. The current code of finding deferreturn PC fails in this case. This CL handles the trampoline as well. Fixes #39049. Change-Id: I929be54d6ae436f5294013793217dc2a35f080d4 Reviewed-on: https://go-review.googlesource.com/c/go/+/234105 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Jeremy Faller <jeremy@golang.org> Reviewed-by: Than McIntosh <thanm@google.com>
1 parent 881d540 commit 2b70ffe

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

src/cmd/link/internal/arm/asm.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,12 +383,16 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
383383
offset := (signext24(r.Add()&0xffffff) + 2) * 4
384384
var tramp loader.Sym
385385
for i := 0; ; i++ {
386-
name := ldr.SymName(rs) + fmt.Sprintf("%+d-tramp%d", offset, i)
386+
oName := ldr.SymName(rs)
387+
name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
387388
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
388389
if ldr.SymType(tramp) == sym.SDYNIMPORT {
389390
// don't reuse trampoline defined in other module
390391
continue
391392
}
393+
if oName == "runtime.deferreturn" {
394+
ldr.SetIsDeferReturnTramp(tramp, true)
395+
}
392396
if ldr.SymValue(tramp) == 0 {
393397
// either the trampoline does not exist -- we need to create one,
394398
// or found one the address which is not assigned -- this will be

src/cmd/link/internal/ld/pcln.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32
161161
// set the resumption point to PC_B.
162162
lastWasmAddr = uint32(r.Add())
163163
}
164-
if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym {
164+
if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
165165
if target.IsWasm() {
166166
deferreturn = lastWasmAddr - 1
167167
} else {

src/cmd/link/internal/loader/loader.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ type Loader struct {
236236
outdata [][]byte // symbol's data in the output buffer
237237
extRelocs [][]ExtReloc // symbol's external relocations
238238

239-
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
239+
itablink map[Sym]struct{} // itablink[j] defined if j is go.itablink.*
240+
deferReturnTramp map[Sym]bool // whether the symbol is a trampoline of a deferreturn call
240241

241242
objByPkg map[string]*oReader // map package path to its Go object reader
242243

@@ -362,6 +363,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
362363
attrCgoExportDynamic: make(map[Sym]struct{}),
363364
attrCgoExportStatic: make(map[Sym]struct{}),
364365
itablink: make(map[Sym]struct{}),
366+
deferReturnTramp: make(map[Sym]bool),
365367
extStaticSyms: make(map[nameVer]Sym),
366368
builtinSyms: make([]Sym, nbuiltin),
367369
flags: flags,
@@ -1062,6 +1064,16 @@ func (l *Loader) IsItabLink(i Sym) bool {
10621064
return false
10631065
}
10641066

1067+
// Return whether this is a trampoline of a deferreturn call.
1068+
func (l *Loader) IsDeferReturnTramp(i Sym) bool {
1069+
return l.deferReturnTramp[i]
1070+
}
1071+
1072+
// Set that i is a trampoline of a deferreturn call.
1073+
func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
1074+
l.deferReturnTramp[i] = v
1075+
}
1076+
10651077
// growValues grows the slice used to store symbol values.
10661078
func (l *Loader) growValues(reqLen int) {
10671079
curLen := len(l.values)

src/cmd/link/internal/ppc64/asm.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -686,16 +686,20 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
686686
// target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
687687
// distinct trampolines.
688688

689-
name := ldr.SymName(rs)
689+
oName := ldr.SymName(rs)
690+
name := oName
690691
if r.Add() == 0 {
691-
name = name + fmt.Sprintf("-tramp%d", i)
692+
name += fmt.Sprintf("-tramp%d", i)
692693
} else {
693-
name = name + fmt.Sprintf("%+x-tramp%d", r.Add(), i)
694+
name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
694695
}
695696

696697
// Look up the trampoline in case it already exists
697698

698699
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
700+
if oName == "runtime.deferreturn" {
701+
ldr.SetIsDeferReturnTramp(tramp, true)
702+
}
699703
if ldr.SymValue(tramp) == 0 {
700704
break
701705
}

src/cmd/link/link_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,23 @@ func TestFuncAlign(t *testing.T) {
629629
}
630630
}
631631

632-
const helloSrc = `
632+
const testTrampSrc = `
633633
package main
634634
import "fmt"
635-
func main() { fmt.Println("hello") }
635+
func main() {
636+
fmt.Println("hello")
637+
638+
defer func(){
639+
if e := recover(); e == nil {
640+
panic("did not panic")
641+
}
642+
}()
643+
f1()
644+
}
645+
646+
// Test deferreturn trampolines. See issue #39049.
647+
func f1() { defer f2() }
648+
func f2() { panic("XXX") }
636649
`
637650

638651
func TestTrampoline(t *testing.T) {
@@ -655,7 +668,7 @@ func TestTrampoline(t *testing.T) {
655668
defer os.RemoveAll(tmpdir)
656669

657670
src := filepath.Join(tmpdir, "hello.go")
658-
err = ioutil.WriteFile(src, []byte(helloSrc), 0666)
671+
err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
659672
if err != nil {
660673
t.Fatal(err)
661674
}

0 commit comments

Comments
 (0)