cmd/compile, cmd/link: use weak reference in itab

When converting a type T to a non-empty interface I, we build the
itab which contains the code pointers of the methods. Currently,
this brings those methods live (if the itab is live), even if the
interface method is never used. This CL changes the itab to use
weak references, so the methods can be pruned if not otherwise
live.

Fixes #42421.

Change-Id: Iee5de2ba11d603c5a102a2ba60440d839a7f9702
Reviewed-on: https://go-review.googlesource.com/c/go/+/268479
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Cherry Zhang 2020-11-08 15:18:35 -05:00
parent 747f426944
commit e8700f1ce6
10 changed files with 39 additions and 2 deletions

View file

@ -46,6 +46,13 @@ func SymPtr(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
return off return off
} }
func SymPtrWeak(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
off = int(types.Rnd(int64(off), int64(types.PtrSize)))
s.WriteWeakAddr(base.Ctxt, int64(off), types.PtrSize, x, int64(xoff))
off += types.PtrSize
return off
}
func SymPtrOff(s *obj.LSym, off int, x *obj.LSym) int { func SymPtrOff(s *obj.LSym, off int, x *obj.LSym) int {
s.WriteOff(base.Ctxt, int64(off), x, 0) s.WriteOff(base.Ctxt, int64(off), x, 0)
off += 4 off += 4

View file

@ -1338,7 +1338,7 @@ func WriteTabs() {
o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash
o += 4 // skip unused field o += 4 // skip unused field
for _, fn := range genfun(i.t, i.itype) { for _, fn := range genfun(i.t, i.itype) {
o = objw.SymPtr(i.lsym, o, fn, 0) // method pointer for each method o = objw.SymPtrWeak(i.lsym, o, fn, 0) // method pointer for each method
} }
// Nothing writes static itabs, so they are read only. // Nothing writes static itabs, so they are read only.
objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA)) objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))

View file

@ -135,6 +135,13 @@ func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64)
s.writeAddr(ctxt, off, siz, rsym, roff, objabi.R_ADDR) s.writeAddr(ctxt, off, siz, rsym, roff, objabi.R_ADDR)
} }
// WriteWeakAddr writes an address of size siz into s at offset off.
// rsym and roff specify the relocation for the address.
// This is a weak reference.
func (s *LSym) WriteWeakAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
s.writeAddr(ctxt, off, siz, rsym, roff, objabi.R_WEAKADDR)
}
// WriteCURelativeAddr writes a pointer-sized address into s at offset off. // WriteCURelativeAddr writes a pointer-sized address into s at offset off.
// rsym and roff specify the relocation for the address which will be // rsym and roff specify the relocation for the address which will be
// resolved by the linker to an offset from the DW_AT_low_pc attribute of // resolved by the linker to an offset from the DW_AT_low_pc attribute of

View file

@ -258,6 +258,7 @@ const (
// reachable. // reachable.
R_WEAK = -1 << 15 R_WEAK = -1 << 15
R_WEAKADDR = R_WEAK | R_ADDR
R_WEAKADDROFF = R_WEAK | R_ADDROFF R_WEAKADDROFF = R_WEAK | R_ADDROFF
) )

View file

@ -306,6 +306,10 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s)) log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", ldr.SymName(s))
} }
case objabi.R_ADDR: case objabi.R_ADDR:
if weak && !ldr.AttrReachable(rs) {
// Redirect it to runtime.unreachableMethod, which will throw if called.
rs = syms.unreachableMethod
}
if target.IsExternal() { if target.IsExternal() {
nExtReloc++ nExtReloc++
@ -586,6 +590,9 @@ func extreloc(ctxt *Link, ldr *loader.Loader, s loader.Sym, r loader.Reloc) (loa
case objabi.R_ADDR: case objabi.R_ADDR:
// set up addend for eventual relocation via outer symbol. // set up addend for eventual relocation via outer symbol.
rs := ldr.ResolveABIAlias(r.Sym()) rs := ldr.ResolveABIAlias(r.Sym())
if r.Weak() && !ldr.AttrReachable(rs) {
rs = ctxt.ArchSyms.unreachableMethod
}
rs, off := FoldSubSymbolOffset(ldr, rs) rs, off := FoldSubSymbolOffset(ldr, rs)
rr.Xadd = r.Add() + off rr.Xadd = r.Add() + off
rr.Xsym = rs rr.Xsym = rs

View file

@ -64,6 +64,9 @@ func (d *deadcodePass) init() {
} }
} }
names = append(names, *flagEntrySymbol) names = append(names, *flagEntrySymbol)
// runtime.unreachableMethod is a function that will throw if called.
// We redirect unreachable methods to it.
names = append(names, "runtime.unreachableMethod")
if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin { if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin {
// runtime.buildVersion and runtime.modinfo are referenced in .go.buildinfo section // runtime.buildVersion and runtime.modinfo are referenced in .go.buildinfo section
// (see function buildinfo in data.go). They should normally be reachable from the // (see function buildinfo in data.go). They should normally be reachable from the

View file

@ -118,6 +118,8 @@ type ArchSyms struct {
Dynamic loader.Sym Dynamic loader.Sym
DynSym loader.Sym DynSym loader.Sym
DynStr loader.Sym DynStr loader.Sym
unreachableMethod loader.Sym
} }
// mkArchSym is a helper for setArchSyms, to set up a special symbol. // mkArchSym is a helper for setArchSyms, to set up a special symbol.
@ -142,6 +144,7 @@ func (ctxt *Link) setArchSyms() {
ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic) ctxt.mkArchSym(".dynamic", 0, &ctxt.Dynamic)
ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym) ctxt.mkArchSym(".dynsym", 0, &ctxt.DynSym)
ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr) ctxt.mkArchSym(".dynstr", 0, &ctxt.DynStr)
ctxt.mkArchSym("runtime.unreachableMethod", sym.SymVerABIInternal, &ctxt.unreachableMethod)
if ctxt.IsPPC64() { if ctxt.IsPPC64() {
ctxt.mkArchSym("TOC", 0, &ctxt.TOC) ctxt.mkArchSym("TOC", 0, &ctxt.TOC)

View file

@ -10,6 +10,7 @@ package main
type T int type T int
//go:noinline
func (T) M() {} func (T) M() {}
type I interface{ M() } type I interface{ M() }
@ -20,4 +21,5 @@ var pp *I
func main() { func main() {
p = new(T) // use type T p = new(T) // use type T
pp = new(I) // use type I pp = new(I) // use type I
*pp = *p // convert T to I, build itab
} }

View file

@ -553,3 +553,10 @@ var staticuint64s = [...]uint64{
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
} }
// The linker redirects a reference of a method that it determined
// unreachable to a reference to this function, so it will throw if
// ever called.
func unreachableMethod() {
throw("unreachable method called. linker bug?")
}

View file

@ -262,7 +262,7 @@ func (t *_type) textOff(off textOff) unsafe.Pointer {
if off == -1 { if off == -1 {
// -1 is the sentinel value for unreachable code. // -1 is the sentinel value for unreachable code.
// See cmd/link/internal/ld/data.go:relocsym. // See cmd/link/internal/ld/data.go:relocsym.
return unsafe.Pointer(^uintptr(0)) return unsafe.Pointer(funcPC(unreachableMethod))
} }
base := uintptr(unsafe.Pointer(t)) base := uintptr(unsafe.Pointer(t))
var md *moduledata var md *moduledata