]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.14] cmd/link: detect trampoline of deferreturn call
authorCherry Zhang <cherryyz@google.com>
Fri, 3 Jul 2020 18:28:15 +0000 (14:28 -0400)
committerDmitri Shuralyov <dmitshur@golang.org>
Fri, 10 Jul 2020 22:07:16 +0000 (22:07 +0000)
This is a backport of CL 234105. This is not a clean cherry-pick,
as CL 234105 is for the new linker, whereas we still use the old
linker here. This CL backports the logic.

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 #39991.
Updates #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>
Reviewed-on: https://go-review.googlesource.com/c/go/+/240917
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Joel Sing <joel@sing.id.au>
src/cmd/link/internal/arm/asm.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ppc64/asm.go
src/cmd/link/internal/sym/attribute.go
src/cmd/link/link_test.go

index f2fb6543d0d939e17264f95a42d6c7c6c33b0764..c4f529a468a2a00cb66ad85a0926655fc745d21f 100644 (file)
@@ -470,8 +470,12 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
                        offset := (signext24(r.Add&0xffffff) + 2) * 4
                        var tramp *sym.Symbol
                        for i := 0; ; i++ {
-                               name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i)
+                               oName := r.Sym.Name
+                               name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
                                tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+                               if oName == "runtime.deferreturn" {
+                                       tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
+                               }
                                if tramp.Type == sym.SDYNIMPORT {
                                        // don't reuse trampoline defined in other module
                                        continue
index 3e8135c959c7d0f6e527028c58eaeb0bccf2d661..43e166191f27100681f5d7e73a5b14b3e36b7675 100644 (file)
@@ -276,7 +276,7 @@ func (ctxt *Link) pclntab() {
                                // set the resumption point to PC_B.
                                lastWasmAddr = uint32(r.Add)
                        }
-                       if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
+                       if r.Type.IsDirectCall() && r.Sym != nil && (r.Sym.Name == "runtime.deferreturn" || r.Sym.Attr.DeferReturnTramp()) {
                                if ctxt.Arch.Family == sys.Wasm {
                                        deferreturn = lastWasmAddr - 1
                                } else {
index 9fbcff551a997432e9e34844ee1dfa08d5ee8105..e84689d1a03cbc1a6a044c7db6a8ef66e23a14af 100644 (file)
@@ -667,7 +667,8 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
                                // target is at some offset within the function.  Calls to duff+8 and duff+256 must appear as
                                // distinct trampolines.
 
-                               name := r.Sym.Name
+                               oName := r.Sym.Name
+                               name := oName
                                if r.Add == 0 {
                                        name = name + fmt.Sprintf("-tramp%d", i)
                                } else {
@@ -677,6 +678,9 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
                                // Look up the trampoline in case it already exists
 
                                tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
+                               if oName == "runtime.deferreturn" {
+                                       tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
+                               }
                                if tramp.Value == 0 {
                                        break
                                }
index 4b69bf32d07f15fbcb288b91c7e60c68c015f2af..773b6a4ee703727dc789434cc80185764c9a3c34 100644 (file)
@@ -81,7 +81,10 @@ const (
        // AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by
        // read-only memory.
        AttrReadOnly
-       // 19 attributes defined so far.
+       // AttrDeferReturnTramp indicates the symbol is a trampoline of a deferreturn
+       // call.
+       AttrDeferReturnTramp
+       // 20 attributes defined so far.
 )
 
 func (a Attribute) DuplicateOK() bool      { return a&AttrDuplicateOK != 0 }
@@ -103,6 +106,7 @@ func (a Attribute) SubSymbol() bool        { return a&AttrSubSymbol != 0 }
 func (a Attribute) Container() bool        { return a&AttrContainer != 0 }
 func (a Attribute) TopFrame() bool         { return a&AttrTopFrame != 0 }
 func (a Attribute) ReadOnly() bool         { return a&AttrReadOnly != 0 }
+func (a Attribute) DeferReturnTramp() bool { return a&AttrDeferReturnTramp != 0 }
 
 func (a Attribute) CgoExport() bool {
        return a.CgoExportDynamic() || a.CgoExportStatic()
index 4f792bd1f19c0512888ae4f1ba2dc1c1f36d2803..f5efb51d296d20ab2ace128788103e3bfbcc9557 100644 (file)
@@ -447,3 +447,66 @@ func TestStrictDup(t *testing.T) {
                t.Errorf("unexpected output:\n%s", out)
        }
 }
+
+const testTrampSrc = `
+package main
+import "fmt"
+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) {
+       // Test that trampoline insertion works as expected.
+       // For stress test, we set -debugtramp=2 flag, which sets a very low
+       // threshold for trampoline generation, and essentially all cross-package
+       // calls will use trampolines.
+       switch runtime.GOARCH {
+       case "arm", "ppc64", "ppc64le":
+       default:
+               t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
+       }
+       if runtime.GOOS == "aix" {
+               t.Skip("trampolines on AIX doesn't work in Go 1.14") // fixed in Go 1.15
+       }
+
+       testenv.MustHaveGoBuild(t)
+
+       tmpdir, err := ioutil.TempDir("", "TestTrampoline")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+
+       src := filepath.Join(tmpdir, "hello.go")
+       err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+       exe := filepath.Join(tmpdir, "hello.exe")
+
+       cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
+       out, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("build failed: %v\n%s", err, out)
+       }
+       cmd = exec.Command(exe)
+       out, err = cmd.CombinedOutput()
+       if err != nil {
+               t.Errorf("executable failed to run: %v\n%s", err, out)
+       }
+       if string(out) != "hello\n" {
+               t.Errorf("unexpected output:\n%s", out)
+       }
+}