mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/link: detect trampoline of deferreturn call
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>
This commit is contained in:
parent
881d540540
commit
2b70ffe930
5 changed files with 42 additions and 9 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ type Loader struct {
|
|||
extRelocs [][]ExtReloc // symbol's external relocations
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue