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