github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/src/cmd/link/internal/ld/deadcode.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ld 6 7 import ( 8 "cmd/internal/obj" 9 "cmd/internal/sys" 10 "flag" 11 "fmt" 12 "path/filepath" 13 "strings" 14 "unicode" 15 ) 16 17 // deadcode marks all reachable symbols. 18 // 19 // The basis of the dead code elimination is a flood fill of symbols, 20 // following their relocations, beginning at *flagEntrySymbol. 21 // 22 // This flood fill is wrapped in logic for pruning unused methods. 23 // All methods are mentioned by relocations on their receiver's *rtype. 24 // These relocations are specially defined as R_METHODOFF by the compiler 25 // so we can detect and manipulated them here. 26 // 27 // There are three ways a method of a reachable type can be invoked: 28 // 29 // 1. direct call 30 // 2. through a reachable interface type 31 // 3. reflect.Value.Call, .Method, or reflect.Method.Func 32 // 33 // The first case is handled by the flood fill, a directly called method 34 // is marked as reachable. 35 // 36 // The second case is handled by decomposing all reachable interface 37 // types into method signatures. Each encountered method is compared 38 // against the interface method signatures, if it matches it is marked 39 // as reachable. This is extremely conservative, but easy and correct. 40 // 41 // The third case is handled by looking to see if any of: 42 // - reflect.Value.Call is reachable 43 // - reflect.Value.Method is reachable 44 // - reflect.Type.Method or MethodByName is called. 45 // If any of these happen, all bets are off and all exported methods 46 // of reachable types are marked reachable. 47 // 48 // Any unreached text symbols are removed from ctxt.Textp. 49 func deadcode(ctxt *Link) { 50 if ctxt.Debugvlog != 0 { 51 ctxt.Logf("%5.2f deadcode\n", obj.Cputime()) 52 } 53 54 d := &deadcodepass{ 55 ctxt: ctxt, 56 ifaceMethod: make(map[methodsig]bool), 57 } 58 59 // First, flood fill any symbols directly reachable in the call 60 // graph from *flagEntrySymbol. Ignore all methods not directly called. 61 d.init() 62 d.flood() 63 64 callSym := ctxt.Syms.ROLookup("reflect.Value.Call", 0) 65 methSym := ctxt.Syms.ROLookup("reflect.Value.Method", 0) 66 reflectSeen := false 67 68 if ctxt.DynlinkingGo() { 69 // Exported methods may satisfy interfaces we don't know 70 // about yet when dynamically linking. 71 reflectSeen = true 72 } 73 74 for { 75 if !reflectSeen { 76 if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) { 77 // Methods might be called via reflection. Give up on 78 // static analysis, mark all exported methods of 79 // all reachable types as reachable. 80 reflectSeen = true 81 } 82 } 83 84 // Mark all methods that could satisfy a discovered 85 // interface as reachable. We recheck old marked interfaces 86 // as new types (with new methods) may have been discovered 87 // in the last pass. 88 var rem []methodref 89 for _, m := range d.markableMethods { 90 if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { 91 d.markMethod(m) 92 } else { 93 rem = append(rem, m) 94 } 95 } 96 d.markableMethods = rem 97 98 if len(d.markQueue) == 0 { 99 // No new work was discovered. Done. 100 break 101 } 102 d.flood() 103 } 104 105 // Remove all remaining unreached R_METHODOFF relocations. 106 for _, m := range d.markableMethods { 107 for _, r := range m.r { 108 d.cleanupReloc(r) 109 } 110 } 111 112 if Buildmode != BuildmodeShared { 113 // Keep a itablink if the symbol it points at is being kept. 114 // (When BuildmodeShared, always keep itablinks.) 115 for _, s := range ctxt.Syms.Allsym { 116 if strings.HasPrefix(s.Name, "go.itablink.") { 117 s.Attr.Set(AttrReachable, len(s.R) == 1 && s.R[0].Sym.Attr.Reachable()) 118 } 119 } 120 } 121 122 // Remove dead text but keep file information (z symbols). 123 textp := make([]*Symbol, 0, len(ctxt.Textp)) 124 for _, s := range ctxt.Textp { 125 if s.Attr.Reachable() { 126 textp = append(textp, s) 127 } 128 } 129 ctxt.Textp = textp 130 } 131 132 var markextra = []string{ 133 "runtime.morestack", 134 "runtime.morestackx", 135 "runtime.morestack00", 136 "runtime.morestack10", 137 "runtime.morestack01", 138 "runtime.morestack11", 139 "runtime.morestack8", 140 "runtime.morestack16", 141 "runtime.morestack24", 142 "runtime.morestack32", 143 "runtime.morestack40", 144 "runtime.morestack48", 145 146 // on arm, lock in the div/mod helpers too 147 "_div", 148 "_divu", 149 "_mod", 150 "_modu", 151 } 152 153 // methodref holds the relocations from a receiver type symbol to its 154 // method. There are three relocations, one for each of the fields in 155 // the reflect.method struct: mtyp, ifn, and tfn. 156 type methodref struct { 157 m methodsig 158 src *Symbol // receiver type symbol 159 r [3]*Reloc // R_METHODOFF relocations to fields of runtime.method 160 } 161 162 func (m methodref) ifn() *Symbol { return m.r[1].Sym } 163 164 func (m methodref) isExported() bool { 165 for _, r := range m.m { 166 return unicode.IsUpper(r) 167 } 168 panic("methodref has no signature") 169 } 170 171 // deadcodepass holds state for the deadcode flood fill. 172 type deadcodepass struct { 173 ctxt *Link 174 markQueue []*Symbol // symbols to flood fill in next pass 175 ifaceMethod map[methodsig]bool // methods declared in reached interfaces 176 markableMethods []methodref // methods of reached types 177 reflectMethod bool 178 } 179 180 func (d *deadcodepass) cleanupReloc(r *Reloc) { 181 if r.Sym.Attr.Reachable() { 182 r.Type = obj.R_ADDROFF 183 } else { 184 if d.ctxt.Debugvlog > 1 { 185 d.ctxt.Logf("removing method %s\n", r.Sym.Name) 186 } 187 r.Sym = nil 188 r.Siz = 0 189 } 190 } 191 192 // mark appends a symbol to the mark queue for flood filling. 193 func (d *deadcodepass) mark(s, parent *Symbol) { 194 if s == nil || s.Attr.Reachable() { 195 return 196 } 197 if s.Attr.ReflectMethod() { 198 d.reflectMethod = true 199 } 200 if *flagDumpDep { 201 p := "_" 202 if parent != nil { 203 p = parent.Name 204 } 205 fmt.Printf("%s -> %s\n", p, s.Name) 206 } 207 s.Attr |= AttrReachable 208 s.Reachparent = parent 209 d.markQueue = append(d.markQueue, s) 210 } 211 212 // markMethod marks a method as reachable. 213 func (d *deadcodepass) markMethod(m methodref) { 214 for _, r := range m.r { 215 d.mark(r.Sym, m.src) 216 r.Type = obj.R_ADDROFF 217 } 218 } 219 220 // init marks all initial symbols as reachable. 221 // In a typical binary, this is *flagEntrySymbol. 222 func (d *deadcodepass) init() { 223 var names []string 224 225 if SysArch.Family == sys.ARM { 226 // mark some functions that are only referenced after linker code editing 227 if obj.GOARM == 5 { 228 names = append(names, "_sfloat") 229 } 230 names = append(names, "runtime.read_tls_fallback") 231 } 232 233 if Buildmode == BuildmodeShared { 234 // Mark all symbols defined in this library as reachable when 235 // building a shared library. 236 for _, s := range d.ctxt.Syms.Allsym { 237 if s.Type != 0 && s.Type != obj.SDYNIMPORT { 238 d.mark(s, nil) 239 } 240 } 241 } else { 242 // In a normal binary, start at main.main and the init 243 // functions and mark what is reachable from there. 244 names = append(names, *flagEntrySymbol) 245 if *FlagLinkshared && (Buildmode == BuildmodeExe || Buildmode == BuildmodePIE) { 246 names = append(names, "main.main", "main.init") 247 } else if Buildmode == BuildmodePlugin { 248 pluginName := strings.TrimSuffix(filepath.Base(flag.Arg(0)), ".a") 249 pluginInit := pluginName + ".init" 250 names = append(names, pluginInit, "go.plugin.tabs") 251 252 // We don't keep the go.plugin.exports symbol, 253 // but we do keep the symbols it refers to. 254 exports := d.ctxt.Syms.ROLookup("go.plugin.exports", 0) 255 for _, r := range exports.R { 256 d.mark(r.Sym, nil) 257 } 258 } 259 for _, name := range markextra { 260 names = append(names, name) 261 } 262 for _, s := range dynexp { 263 d.mark(s, nil) 264 } 265 } 266 267 for _, name := range names { 268 d.mark(d.ctxt.Syms.ROLookup(name, 0), nil) 269 } 270 } 271 272 // flood flood fills symbols reachable from the markQueue symbols. 273 // As it goes, it collects methodref and interface method declarations. 274 func (d *deadcodepass) flood() { 275 for len(d.markQueue) > 0 { 276 s := d.markQueue[0] 277 d.markQueue = d.markQueue[1:] 278 if s.Type == obj.STEXT { 279 if d.ctxt.Debugvlog > 1 { 280 d.ctxt.Logf("marktext %s\n", s.Name) 281 } 282 if s.FuncInfo != nil { 283 for _, a := range s.FuncInfo.Autom { 284 d.mark(a.Gotype, s) 285 } 286 } 287 288 } 289 290 if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' { 291 if decodetypeKind(s)&kindMask == kindInterface { 292 for _, sig := range decodeIfaceMethods(d.ctxt.Arch, s) { 293 if d.ctxt.Debugvlog > 1 { 294 d.ctxt.Logf("reached iface method: %s\n", sig) 295 } 296 d.ifaceMethod[sig] = true 297 } 298 } 299 } 300 301 mpos := 0 // 0-3, the R_METHODOFF relocs of runtime.uncommontype 302 var methods []methodref 303 for i := 0; i < len(s.R); i++ { 304 r := &s.R[i] 305 if r.Sym == nil { 306 continue 307 } 308 if r.Type != obj.R_METHODOFF { 309 d.mark(r.Sym, s) 310 continue 311 } 312 // Collect rtype pointers to methods for 313 // later processing in deadcode. 314 if mpos == 0 { 315 m := methodref{src: s} 316 m.r[0] = r 317 methods = append(methods, m) 318 } else { 319 methods[len(methods)-1].r[mpos] = r 320 } 321 mpos++ 322 if mpos == len(methodref{}.r) { 323 mpos = 0 324 } 325 } 326 if len(methods) > 0 { 327 // Decode runtime type information for type methods 328 // to help work out which methods can be called 329 // dynamically via interfaces. 330 methodsigs := decodetypeMethods(d.ctxt.Arch, s) 331 if len(methods) != len(methodsigs) { 332 panic(fmt.Sprintf("%q has %d method relocations for %d methods", s.Name, len(methods), len(methodsigs))) 333 } 334 for i, m := range methodsigs { 335 name := string(m) 336 name = name[:strings.Index(name, "(")] 337 if !strings.HasSuffix(methods[i].ifn().Name, name) { 338 panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name)) 339 } 340 methods[i].m = m 341 } 342 d.markableMethods = append(d.markableMethods, methods...) 343 } 344 345 if s.FuncInfo != nil { 346 for i := range s.FuncInfo.Funcdata { 347 d.mark(s.FuncInfo.Funcdata[i], s) 348 } 349 } 350 d.mark(s.Gotype, s) 351 d.mark(s.Sub, s) 352 d.mark(s.Outer, s) 353 } 354 }