]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: detect trampoline of deferreturn call
authorCherry Zhang <cherryyz@google.com>
Thu, 14 May 2020 23:22:59 +0000 (19:22 -0400)
committerCherry Zhang <cherryyz@google.com>
Fri, 15 May 2020 16:15:25 +0000 (16:15 +0000)
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>
src/cmd/link/internal/arm/asm.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/loader/loader.go
src/cmd/link/internal/ppc64/asm.go
src/cmd/link/link_test.go

index a2024bcedec76df36714994162039b950759a61f..d28af163c7eee08a6a8aa2635a66565ce71a0f4e 100644 (file)
@@ -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
index 00c29c63e071bd125b722959ab5c9850fe7f6df8..5cbb7bbaccb44af7d0b6f2fa01d3850464843a4f 100644 (file)
@@ -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 {
index 2627218ced85758f9babc4509761127d81c65faf..8e6451d270e767febb414f9b5f26c926d84bf575 100644 (file)
@@ -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
 
@@ -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,
@@ -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)
index bd4827ecb54bf4e441e48fe131bb2b5d42d69b2b..fc714c9dd1c8822dab8ca98d9798af2481267e81 100644 (file)
@@ -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
                                }
index 1c9e1779116a6706a6cdc6facf0118b38ef41984..5ff9912a11a886c90e6c76b6ae7a8b479378463c 100644 (file)
@@ -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) {
@@ -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)
        }