aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link/internal/ld/deadcode2.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/ld/deadcode2.go')
-rw-r--r--src/cmd/link/internal/ld/deadcode2.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go
new file mode 100644
index 0000000000..373cffc25e
--- /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)
+ }
+ }
+ }
+}