var _ … type deadcodePass … func (d *deadcodePass) init() { … } func (d *deadcodePass) flood() { … } // mapinitcleanup walks all pkg init functions and looks for weak relocations // to mapinit symbols that are no longer reachable. It rewrites // the relocs to target a new no-op routine in the runtime. func (d *deadcodePass) mapinitcleanup() { … } func (d *deadcodePass) mark(symIdx, parent loader.Sym) { … } func (d *deadcodePass) dumpDepAddFlags(name string, symIdx loader.Sym) string { … } func (d *deadcodePass) markMethod(m methodref) { … } // deadcode marks all reachable symbols. // // The basis of the dead code elimination is a flood fill of symbols, // following their relocations, beginning at *flagEntrySymbol. // // This flood fill is wrapped in logic for pruning unused methods. // All methods are mentioned by relocations on their receiver's *rtype. // These relocations are specially defined as R_METHODOFF by the compiler // so we can detect and manipulated them here. // // There are three ways a method of a reachable type can be invoked: // // 1. direct call // 2. through a reachable interface type // 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method // (or MethodByName) // // The first case is handled by the flood fill, a directly called method // is marked as reachable. // // The second case is handled by decomposing all reachable interface // types into method signatures. Each encountered method is compared // against the interface method signatures, if it matches it is marked // as reachable. This is extremely conservative, but easy and correct. // // The third case is handled by looking for functions that compiler flagged // as REFLECTMETHOD. REFLECTMETHOD on a function F means that F does a method // lookup with reflection, but the compiler was not able to statically determine // the method name. // // All functions that call reflect.Value.Method or reflect.Type.Method are REFLECTMETHODs. // Functions that call reflect.Value.MethodByName or reflect.Type.MethodByName with // a non-constant argument are REFLECTMETHODs, too. If we find a REFLECTMETHOD, // we give up on static analysis, and mark all exported methods of all reachable // types as reachable. // // If the argument to MethodByName is a compile-time constant, the compiler // emits a relocation with the method name. Matching methods are kept in all // reachable types. // // Any unreached text symbols are removed from ctxt.Textp. func deadcode(ctxt *Link) { … } type methodsig … type methodref … func (m methodref) isExported() bool { … } // decodeMethodSig decodes an array of method signature information. // Each element of the array is size bytes. The first 4 bytes is a // nameOff for the method name, and the next 4 bytes is a typeOff for // the function type. // // Conveniently this is the layout of both runtime.method and runtime.imethod. func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig { … } // Decode the method of interface type symbol symIdx at offset off. func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig { … } // Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name. func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string { … } func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { … }