diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index a024e40dffc94df2f6827b3dde3aeba3e46075ec..d0896fcf2c24a4536e93576f64cb88c1369d7893 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -50,6 +50,11 @@ func deadcode(ctxt *Link) { ctxt.Logf("%5.2f deadcode\n", Cputime()) } + if *flagNewobj { + deadcode2(ctxt) + return + } + d := &deadcodepass{ ctxt: ctxt, ifaceMethod: make(map[methodsig]bool), @@ -118,6 +123,10 @@ func deadcode(ctxt *Link) { } } + addToTextp(ctxt) +} + +func addToTextp(ctxt *Link) { // Remove dead text but keep file information (z symbols). textp := []*sym.Symbol{} for _, s := range ctxt.Textp { diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go new file mode 100644 index 0000000000000000000000000000000000000000..373cffc25eae64958131a4ac3fa08a6ea519bc8d --- /dev/null +++ b/src/cmd/link/internal/ld/deadcode2.go @@ -0,0 +1,144 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/objfile" + "cmd/link/internal/sym" + "fmt" + "strings" +) + +var _ = fmt.Print + +// TODO: +// - Live method tracking: +// Prune methods that are not directly called and cannot +// be potentially called by interface or reflect call. +// For now, all the methods from reachable type are alive. +// - Shared object support: +// It basically marks everything. We could consider using +// a different mechanism to represent it. +// - Field tracking support: +// It needs to record from where the symbol is referenced. + +type workQueue []objfile.Sym + +func (q *workQueue) push(i objfile.Sym) { *q = append(*q, i) } +func (q *workQueue) pop() objfile.Sym { i := (*q)[len(*q)-1]; *q = (*q)[:len(*q)-1]; return i } +func (q *workQueue) empty() bool { return len(*q) == 0 } + +type deadcodePass2 struct { + ctxt *Link + loader *objfile.Loader + wq workQueue +} + +func (d *deadcodePass2) init() { + d.loader.InitReachable() + + var names []string + + // In a normal binary, start at main.main and the init + // functions and mark what is reachable from there. + if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + names = append(names, "main.main", "main..inittask") + } else { + // The external linker refers main symbol directly. + if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) { + if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 { + *flagEntrySymbol = "_main" + } else { + *flagEntrySymbol = "main" + } + } + names = append(names, *flagEntrySymbol) + if d.ctxt.BuildMode == BuildModePlugin { + names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs") + + // We don't keep the go.plugin.exports symbol, + // but we do keep the symbols it refers to. + exportsIdx := d.loader.Lookup("go.plugin.exports", 0) + if exportsIdx != 0 { + nreloc := d.loader.NReloc(exportsIdx) + for i := 0; i < nreloc; i++ { + d.mark(d.loader.RelocSym(exportsIdx, i)) + } + } + } + } + for _, s := range dynexp { + d.mark(d.loader.Lookup(s.Name, int(s.Version))) + } + + for _, name := range names { + // Mark symbol as an data/ABI0 symbol. + d.mark(d.loader.Lookup(name, 0)) + // Also mark any Go functions (internal ABI). + d.mark(d.loader.Lookup(name, sym.SymVerABIInternal)) + } +} + +func (d *deadcodePass2) flood() { + for !d.wq.empty() { + symIdx := d.wq.pop() + nreloc := d.loader.NReloc(symIdx) + for i := 0; i < nreloc; i++ { + t := d.loader.RelocType(symIdx, i) + if t == objabi.R_WEAKADDROFF { + continue + } + if t == objabi.R_METHODOFF { + // TODO: we should do something about it + // For now, all the methods are considered live + } + d.mark(d.loader.RelocSym(symIdx, i)) + } + naux := d.loader.NAux(symIdx) + for i := 0; i < naux; i++ { + d.mark(d.loader.AuxSym(symIdx, i)) + } + } +} + +func (d *deadcodePass2) mark(symIdx objfile.Sym) { + if symIdx != 0 && !d.loader.Reachable.Has(symIdx) { + d.wq.push(symIdx) + d.loader.Reachable.Set(symIdx) + } +} + +func deadcode2(ctxt *Link) { + loader := ctxt.loader + d := deadcodePass2{ctxt: ctxt, loader: loader} + d.init() + d.flood() + + n := loader.NSym() + if ctxt.BuildMode != BuildModeShared { + // Keep a itablink if the symbol it points at is being kept. + // (When BuildModeShared, always keep itablinks.) + for i := 1; i < n; i++ { + s := objfile.Sym(i) + if strings.HasPrefix(loader.RawSymName(s), "go.itablink.") { + if d.loader.NReloc(s) > 0 && loader.Reachable.Has(loader.RelocSym(s, 0)) { + loader.Reachable.Set(s) + } + } + } + } + + // Set reachable attr for now. + for i := 1; i < n; i++ { + if loader.Reachable.Has(objfile.Sym(i)) { + s := loader.Syms[i] + if s != nil && s.Name != "" { + s.Attr.Set(sym.AttrReachable, true) + } + } + } +} diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index b913479b723dedaa57b4cd12c51d2b767d69e3f0..d030340cc0e3201785cd8ac5da6514a521724510 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -543,11 +543,6 @@ func (ctxt *Link) loadlib() { ctxt.Loaded = true importcycles() - - // For now, load relocations for dead-code elimination. - if *flagNewobj { - objfile.LoadReloc(ctxt.loader) - } } // Set up flags and special symbols depending on the platform build mode. @@ -2537,6 +2532,7 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library func (ctxt *Link) loadlibfull() { // Load full symbol contents, resolve indexed references. + objfile.LoadReloc(ctxt.loader) objfile.LoadFull(ctxt.loader) // For now, add all symbols to ctxt.Syms. @@ -2548,6 +2544,8 @@ func (ctxt *Link) loadlibfull() { // Drop the reference. ctxt.loader = nil + + addToTextp(ctxt) } func (ctxt *Link) dumpsyms() { diff --git a/src/cmd/link/internal/objfile/objfile2.go b/src/cmd/link/internal/objfile/objfile2.go index 5bc73460968b2cbdc8967b1a106cc904f0b18520..2be34b823e0962c4e0eb37d4bc27f084dfba4d03 100644 --- a/src/cmd/link/internal/objfile/objfile2.go +++ b/src/cmd/link/internal/objfile/objfile2.go @@ -47,19 +47,40 @@ type nameVer struct { v int } +type bitmap []uint32 + +// set the i-th bit. +func (bm bitmap) Set(i Sym) { + n, r := uint(i)/32, uint(i)%32 + bm[n] |= 1 << r +} + +// whether the i-th bit is set. +func (bm bitmap) Has(i Sym) bool { + n, r := uint(i)/32, uint(i)%32 + return bm[n]&(1<<r) != 0 +} + +func makeBitmap(n int) bitmap { + return make(bitmap, (n+31)/32) +} + // A Loader loads new object files and resolves indexed symbol references. // // TODO: describe local-global index mapping. type Loader struct { - start map[*oReader]Sym // map from object file to its start index - objs []objIdx // sorted by start index (i.e. objIdx.i) - max Sym // current max index + start map[*oReader]Sym // map from object file to its start index + objs []objIdx // sorted by start index (i.e. objIdx.i) + max Sym // current max index + extStart Sym // from this index on, the symbols are externally defined symsByName map[nameVer]Sym // map symbol name to index objByPkg map[string]*oReader // map package path to its Go object reader Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now. + + Reachable bitmap // bitmap of reachable symbols, indexed by global index } func NewLoader() *Loader { @@ -95,6 +116,9 @@ func (l *Loader) AddObj(pkg string, r *oReader) Sym { // Add a symbol with a given index, return if it is added. func (l *Loader) AddSym(name string, ver int, i Sym, dupok bool) bool { + if l.extStart != 0 { + panic("AddSym called after AddExtSym is called") + } nv := nameVer{name, ver} if _, ok := l.symsByName[nv]; ok { if dupok || true { // TODO: "true" isn't quite right. need to implement "overwrite" logic. @@ -116,6 +140,9 @@ func (l *Loader) AddExtSym(name string, ver int) Sym { i := l.max + 1 l.symsByName[nv] = i l.max++ + if l.extStart == 0 { + l.extStart = i + } return i } @@ -126,13 +153,16 @@ func (l *Loader) ToGlobal(r *oReader, i int) Sym { // Convert a global index to a local index. func (l *Loader) ToLocal(i Sym) (*oReader, int) { - k := sort.Search(int(i), func(k int) bool { - return l.objs[k].i >= i - }) - if k == len(l.objs) { - return nil, 0 + if l.extStart != 0 && i >= l.extStart { + return nil, int(i - l.extStart) } - return l.objs[k].r, int(i - l.objs[k].i) + // Search for the local object holding index i. + // Below k is the first one that has its start index > i, + // so k-1 is the one we want. + k := sort.Search(len(l.objs), func(k int) bool { + return l.objs[k].i > i + }) + return l.objs[k-1].r, int(i - l.objs[k-1].i) } // Resolve a local symbol reference. Return global index. @@ -172,6 +202,94 @@ func (l *Loader) Lookup(name string, ver int) Sym { return l.symsByName[nv] } +// Number of total symbols. +func (l *Loader) NSym() int { + return int(l.max + 1) +} + +// Returns the raw (unpatched) name of the i-th symbol. +func (l *Loader) RawSymName(i Sym) string { + r, li := l.ToLocal(i) + if r == nil { + return "" + } + osym := goobj2.Sym{} + osym.Read(r.Reader, r.SymOff(li)) + return osym.Name +} + +// Returns the (patched) name of the i-th symbol. +func (l *Loader) SymName(i Sym) string { + r, li := l.ToLocal(i) + if r == nil { + return "" + } + osym := goobj2.Sym{} + osym.Read(r.Reader, r.SymOff(li)) + return strings.Replace(osym.Name, "\"\".", r.pkgprefix, -1) +} + +// Returns the type of the i-th symbol. +func (l *Loader) SymType(i Sym) sym.SymKind { + r, li := l.ToLocal(i) + if r == nil { + return 0 + } + osym := goobj2.Sym{} + osym.Read(r.Reader, r.SymOff(li)) + return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)] +} + +// Returns the number of relocations given a global index. +func (l *Loader) NReloc(i Sym) int { + r, li := l.ToLocal(i) + if r == nil { + return 0 + } + return r.NReloc(li) +} + +// Returns the referred symbol of the j-th relocation of the i-th +// symbol. +func (l *Loader) RelocSym(i Sym, j int) Sym { + r, li := l.ToLocal(i) + rel := goobj2.Reloc{} + rel.Read(r.Reader, r.RelocOff(li, j)) + return l.Resolve(r, rel.Sym) +} + +// Returns the relocation type of the j-th relocation of the i-th +// symbol. +func (l *Loader) RelocType(i Sym, j int) objabi.RelocType { + r, li := l.ToLocal(i) + rel := goobj2.Reloc{} + rel.Read(r.Reader, r.RelocOff(li, j)) + return objabi.RelocType(rel.Type) +} + +// Returns the number of aux symbols given a global index. +func (l *Loader) NAux(i Sym) int { + r, li := l.ToLocal(i) + if r == nil { + return 0 + } + return r.NAux(li) +} + +// Returns the referred symbol of the j-th aux symbol of the i-th +// symbol. +func (l *Loader) AuxSym(i Sym, j int) Sym { + r, li := l.ToLocal(i) + a := goobj2.Aux{} + a.Read(r.Reader, r.AuxOff(li, j)) + return l.Resolve(r, a.Sym) +} + +// Initialize Reachable bitmap for running deadcode pass. +func (l *Loader) InitReachable() { + l.Reachable = makeBitmap(l.NSym()) +} + // Preload a package: add autolibs, add symbols to the symbol table. // Does not read symbol data yet. func LoadNew(l *Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) { @@ -338,6 +456,12 @@ func loadObjReloc(l *Loader, r *oReader) { if t == 0 { log.Fatalf("missing type for %s in %s", s.Name, lib) } + if !s.Attr.Reachable() && (t < sym.SDWARFSECT || t > sym.SDWARFLINES) && !(t == sym.SRODATA && strings.HasPrefix(name, "type.")) { + // No need to load unreachable symbols. + // XXX DWARF symbols may be used but are not marked reachable. + // XXX type symbol's content may be needed in DWARF code, but they are not marked. + continue + } if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) { t = s.Type } @@ -350,22 +474,33 @@ func loadObjReloc(l *Loader, r *oReader) { for j := range s.R { rel := goobj2.Reloc{} rel.Read(r.Reader, r.RelocOff(i, j)) + rs := l.Resolve(r, rel.Sym) + rt := objabi.RelocType(rel.Type) + sz := rel.Siz + if rt == objabi.R_METHODOFF { + if l.Reachable.Has(rs) { + rt = objabi.R_ADDROFF + } else { + sz = 0 + rs = 0 + } + } + if rt == objabi.R_WEAKADDROFF && !l.Reachable.Has(rs) { + rs = 0 + sz = 0 + } + if rs != 0 && l.SymType(rs) == sym.SABIALIAS { + rs = l.RelocSym(rs, 0) + } s.R[j] = sym.Reloc{ Off: rel.Off, - Siz: rel.Siz, - Type: objabi.RelocType(rel.Type), + Siz: sz, + Type: rt, Add: rel.Add, - Sym: resolveSymRef(rel.Sym), + Sym: l.Syms[rs], } } - // XXX deadcode needs symbol data for type symbols. Read it now. - if strings.HasPrefix(name, "type.") { - s.P = r.Data(i) - s.Attr.Set(sym.AttrReadOnly, r.ReadOnly()) - s.Size = int64(osym.Siz) - } - // Aux symbol naux := r.NAux(i) for j := 0; j < naux; j++ { @@ -430,9 +565,10 @@ func loadObjFull(l *Loader, r *oReader) { if s == nil || s.Name == "" { continue } - if !s.Attr.Reachable() && (s.Type < sym.SDWARFSECT || s.Type > sym.SDWARFLINES) { + if !s.Attr.Reachable() && (s.Type < sym.SDWARFSECT || s.Type > sym.SDWARFLINES) && !(s.Type == sym.SRODATA && strings.HasPrefix(s.Name, "type.")) { // No need to load unreachable symbols. // XXX DWARF symbols may be used but are not marked reachable. + // XXX type symbol's content may be needed in DWARF code, but they are not marked. continue }