Skip to content

Commit

Permalink
[release-branch.go1.14] cmd/link: detect trampoline of deferreturn call
Browse files Browse the repository at this point in the history
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 <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Jeremy Faller <[email protected]>
Reviewed-by: Than McIntosh <[email protected]>
Reviewed-on: https://go-review.googlesource.com/c/go/+/240917
Run-TryBot: Dmitri Shuralyov <[email protected]>
Reviewed-by: Austin Clements <[email protected]>
Reviewed-by: Joel Sing <[email protected]>
  • Loading branch information
cherrymui authored and dmitshur committed Jul 10, 2020
1 parent be0254a commit b6f70c0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 4 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 @@ -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
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 @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion src/cmd/link/internal/ppc64/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down
6 changes: 5 additions & 1 deletion src/cmd/link/internal/sym/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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()
Expand Down
63 changes: 63 additions & 0 deletions src/cmd/link/link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit b6f70c0

Please sign in to comment.