Skip to content

Commit

Permalink
cmd/link: detect trampoline of deferreturn call
Browse files Browse the repository at this point in the history
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 <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Jeremy Faller <[email protected]>
Reviewed-by: Than McIntosh <[email protected]>
  • Loading branch information
cherrymui committed May 15, 2020
1 parent 881d540 commit 2b70ffe
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 9 deletions.
6 changes: 5 additions & 1 deletion src/cmd/link/internal/arm/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,16 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
offset := (signext24(r.Add()&0xffffff) + 2) * 4
var tramp loader.Sym
for i := 0; ; i++ {
name := ldr.SymName(rs) + fmt.Sprintf("%+d-tramp%d", offset, i)
oName := ldr.SymName(rs)
name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
if ldr.SymType(tramp) == sym.SDYNIMPORT {
// don't reuse trampoline defined in other module
continue
}
if oName == "runtime.deferreturn" {
ldr.SetIsDeferReturnTramp(tramp, true)
}
if ldr.SymValue(tramp) == 0 {
// either the trampoline does not exist -- we need to create one,
// or found one the address which is not assigned -- this will be
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/link/internal/ld/pcln.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add())
}
if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym {
if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
if target.IsWasm() {
deferreturn = lastWasmAddr - 1
} else {
Expand Down
14 changes: 13 additions & 1 deletion src/cmd/link/internal/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ type Loader struct {
outdata [][]byte // symbol's data in the output buffer
extRelocs [][]ExtReloc // symbol's external relocations

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

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

Expand Down Expand Up @@ -362,6 +363,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor
attrCgoExportDynamic: make(map[Sym]struct{}),
attrCgoExportStatic: make(map[Sym]struct{}),
itablink: make(map[Sym]struct{}),
deferReturnTramp: make(map[Sym]bool),
extStaticSyms: make(map[nameVer]Sym),
builtinSyms: make([]Sym, nbuiltin),
flags: flags,
Expand Down Expand Up @@ -1062,6 +1064,16 @@ func (l *Loader) IsItabLink(i Sym) bool {
return false
}

// Return whether this is a trampoline of a deferreturn call.
func (l *Loader) IsDeferReturnTramp(i Sym) bool {
return l.deferReturnTramp[i]
}

// Set that i is a trampoline of a deferreturn call.
func (l *Loader) SetIsDeferReturnTramp(i Sym, v bool) {
l.deferReturnTramp[i] = v
}

// growValues grows the slice used to store symbol values.
func (l *Loader) growValues(reqLen int) {
curLen := len(l.values)
Expand Down
10 changes: 7 additions & 3 deletions src/cmd/link/internal/ppc64/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,16 +686,20 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
// target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
// distinct trampolines.

name := ldr.SymName(rs)
oName := ldr.SymName(rs)
name := oName
if r.Add() == 0 {
name = name + fmt.Sprintf("-tramp%d", i)
name += fmt.Sprintf("-tramp%d", i)
} else {
name = name + fmt.Sprintf("%+x-tramp%d", r.Add(), i)
name += fmt.Sprintf("%+x-tramp%d", r.Add(), i)
}

// Look up the trampoline in case it already exists

tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
if oName == "runtime.deferreturn" {
ldr.SetIsDeferReturnTramp(tramp, true)
}
if ldr.SymValue(tramp) == 0 {
break
}
Expand Down
19 changes: 16 additions & 3 deletions src/cmd/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,10 +629,23 @@ func TestFuncAlign(t *testing.T) {
}
}

const helloSrc = `
const testTrampSrc = `
package main
import "fmt"
func main() { fmt.Println("hello") }
func main() {
fmt.Println("hello")
defer func(){
if e := recover(); e == nil {
panic("did not panic")
}
}()
f1()
}
// Test deferreturn trampolines. See issue #39049.
func f1() { defer f2() }
func f2() { panic("XXX") }
`

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

src := filepath.Join(tmpdir, "hello.go")
err = ioutil.WriteFile(src, []byte(helloSrc), 0666)
err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 2b70ffe

Please sign in to comment.