Skip to content

Commit b6f70c0

Browse files
cherrymuidmitshur
authored andcommitted
[release-branch.go1.14] cmd/link: detect trampoline of deferreturn call
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]>
1 parent be0254a commit b6f70c0

File tree

5 files changed

+79
-4
lines changed

5 files changed

+79
-4
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,12 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
470470
offset := (signext24(r.Add&0xffffff) + 2) * 4
471471
var tramp *sym.Symbol
472472
for i := 0; ; i++ {
473-
name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i)
473+
oName := r.Sym.Name
474+
name := oName + fmt.Sprintf("%+d-tramp%d", offset, i)
474475
tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
476+
if oName == "runtime.deferreturn" {
477+
tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
478+
}
475479
if tramp.Type == sym.SDYNIMPORT {
476480
// don't reuse trampoline defined in other module
477481
continue

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func (ctxt *Link) pclntab() {
276276
// set the resumption point to PC_B.
277277
lastWasmAddr = uint32(r.Add)
278278
}
279-
if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
279+
if r.Type.IsDirectCall() && r.Sym != nil && (r.Sym.Name == "runtime.deferreturn" || r.Sym.Attr.DeferReturnTramp()) {
280280
if ctxt.Arch.Family == sys.Wasm {
281281
deferreturn = lastWasmAddr - 1
282282
} else {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,8 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
667667
// target is at some offset within the function. Calls to duff+8 and duff+256 must appear as
668668
// distinct trampolines.
669669

670-
name := r.Sym.Name
670+
oName := r.Sym.Name
671+
name := oName
671672
if r.Add == 0 {
672673
name = name + fmt.Sprintf("-tramp%d", i)
673674
} else {
@@ -677,6 +678,9 @@ func trampoline(ctxt *ld.Link, r *sym.Reloc, s *sym.Symbol) {
677678
// Look up the trampoline in case it already exists
678679

679680
tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version))
681+
if oName == "runtime.deferreturn" {
682+
tramp.Attr.Set(sym.AttrDeferReturnTramp, true)
683+
}
680684
if tramp.Value == 0 {
681685
break
682686
}

src/cmd/link/internal/sym/attribute.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ const (
8181
// AttrReadOnly indicates whether the symbol's content (Symbol.P) is backed by
8282
// read-only memory.
8383
AttrReadOnly
84-
// 19 attributes defined so far.
84+
// AttrDeferReturnTramp indicates the symbol is a trampoline of a deferreturn
85+
// call.
86+
AttrDeferReturnTramp
87+
// 20 attributes defined so far.
8588
)
8689

8790
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
@@ -103,6 +106,7 @@ func (a Attribute) SubSymbol() bool { return a&AttrSubSymbol != 0 }
103106
func (a Attribute) Container() bool { return a&AttrContainer != 0 }
104107
func (a Attribute) TopFrame() bool { return a&AttrTopFrame != 0 }
105108
func (a Attribute) ReadOnly() bool { return a&AttrReadOnly != 0 }
109+
func (a Attribute) DeferReturnTramp() bool { return a&AttrDeferReturnTramp != 0 }
106110

107111
func (a Attribute) CgoExport() bool {
108112
return a.CgoExportDynamic() || a.CgoExportStatic()

src/cmd/link/link_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,66 @@ func TestStrictDup(t *testing.T) {
447447
t.Errorf("unexpected output:\n%s", out)
448448
}
449449
}
450+
451+
const testTrampSrc = `
452+
package main
453+
import "fmt"
454+
func main() {
455+
fmt.Println("hello")
456+
457+
defer func(){
458+
if e := recover(); e == nil {
459+
panic("did not panic")
460+
}
461+
}()
462+
f1()
463+
}
464+
465+
// Test deferreturn trampolines. See issue #39049.
466+
func f1() { defer f2() }
467+
func f2() { panic("XXX") }
468+
`
469+
470+
func TestTrampoline(t *testing.T) {
471+
// Test that trampoline insertion works as expected.
472+
// For stress test, we set -debugtramp=2 flag, which sets a very low
473+
// threshold for trampoline generation, and essentially all cross-package
474+
// calls will use trampolines.
475+
switch runtime.GOARCH {
476+
case "arm", "ppc64", "ppc64le":
477+
default:
478+
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
479+
}
480+
if runtime.GOOS == "aix" {
481+
t.Skip("trampolines on AIX doesn't work in Go 1.14") // fixed in Go 1.15
482+
}
483+
484+
testenv.MustHaveGoBuild(t)
485+
486+
tmpdir, err := ioutil.TempDir("", "TestTrampoline")
487+
if err != nil {
488+
t.Fatal(err)
489+
}
490+
defer os.RemoveAll(tmpdir)
491+
492+
src := filepath.Join(tmpdir, "hello.go")
493+
err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
494+
if err != nil {
495+
t.Fatal(err)
496+
}
497+
exe := filepath.Join(tmpdir, "hello.exe")
498+
499+
cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
500+
out, err := cmd.CombinedOutput()
501+
if err != nil {
502+
t.Fatalf("build failed: %v\n%s", err, out)
503+
}
504+
cmd = exec.Command(exe)
505+
out, err = cmd.CombinedOutput()
506+
if err != nil {
507+
t.Errorf("executable failed to run: %v\n%s", err, out)
508+
}
509+
if string(out) != "hello\n" {
510+
t.Errorf("unexpected output:\n%s", out)
511+
}
512+
}

0 commit comments

Comments
 (0)