1  
     2  
     3  
     4  
     5  package ld
     6  
     7  import (
     8  	"cmd/internal/goobj"
     9  	"cmd/internal/objabi"
    10  	"cmd/internal/sys"
    11  	"cmd/link/internal/loader"
    12  	"cmd/link/internal/sym"
    13  	"fmt"
    14  	"internal/abi"
    15  	"internal/buildcfg"
    16  	"strings"
    17  	"unicode"
    18  )
    19  
    20  var _ = fmt.Print
    21  
    22  type deadcodePass struct {
    23  	ctxt *Link
    24  	ldr  *loader.Loader
    25  	wq   heap 
    26  
    27  	ifaceMethod        map[methodsig]bool 
    28  	genericIfaceMethod map[string]bool    
    29  	markableMethods    []methodref        
    30  	reflectSeen        bool               
    31  	dynlink            bool
    32  
    33  	methodsigstmp []methodsig 
    34  	pkginits      []loader.Sym
    35  	mapinitnoop   loader.Sym
    36  }
    37  
    38  func (d *deadcodePass) init() {
    39  	d.ldr.InitReachable()
    40  	d.ifaceMethod = make(map[methodsig]bool)
    41  	d.genericIfaceMethod = make(map[string]bool)
    42  	if buildcfg.Experiment.FieldTrack {
    43  		d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
    44  	}
    45  	d.dynlink = d.ctxt.DynlinkingGo()
    46  
    47  	if d.ctxt.BuildMode == BuildModeShared {
    48  		
    49  		
    50  		n := d.ldr.NDef()
    51  		for i := 1; i < n; i++ {
    52  			s := loader.Sym(i)
    53  			if d.ldr.SymType(s).IsText() && d.ldr.SymSize(s) == 0 {
    54  				
    55  				
    56  				
    57  				continue
    58  			}
    59  			d.mark(s, 0)
    60  		}
    61  		d.mark(d.ctxt.mainInittasks, 0)
    62  		return
    63  	}
    64  
    65  	var names []string
    66  
    67  	
    68  	
    69  	if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
    70  		names = append(names, "main.main", "main..inittask")
    71  	} else {
    72  		
    73  		if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
    74  			if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
    75  				*flagEntrySymbol = "_main"
    76  			} else {
    77  				*flagEntrySymbol = "main"
    78  			}
    79  		}
    80  		names = append(names, *flagEntrySymbol)
    81  	}
    82  	
    83  	
    84  	names = append(names, "runtime.unreachableMethod")
    85  	if d.ctxt.BuildMode == BuildModePlugin {
    86  		names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go:plugin.tabs")
    87  
    88  		
    89  		
    90  		exportsIdx := d.ldr.Lookup("go:plugin.exports", 0)
    91  		if exportsIdx != 0 {
    92  			relocs := d.ldr.Relocs(exportsIdx)
    93  			for i := 0; i < relocs.Count(); i++ {
    94  				d.mark(relocs.At(i).Sym(), 0)
    95  			}
    96  		}
    97  	}
    98  
    99  	if d.ctxt.Debugvlog > 1 {
   100  		d.ctxt.Logf("deadcode start names: %v\n", names)
   101  	}
   102  
   103  	for _, name := range names {
   104  		
   105  		d.mark(d.ldr.Lookup(name, 0), 0)
   106  		if abiInternalVer != 0 {
   107  			
   108  			d.mark(d.ldr.Lookup(name, abiInternalVer), 0)
   109  		}
   110  	}
   111  
   112  	
   113  	for _, s := range d.ctxt.dynexp {
   114  		if d.ctxt.Debugvlog > 1 {
   115  			d.ctxt.Logf("deadcode start dynexp: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s))
   116  		}
   117  		d.mark(s, 0)
   118  	}
   119  	
   120  	for _, s := range d.ldr.WasmExports {
   121  		if d.ctxt.Debugvlog > 1 {
   122  			d.ctxt.Logf("deadcode start wasmexport: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s))
   123  		}
   124  		d.mark(s, 0)
   125  	}
   126  
   127  	d.mapinitnoop = d.ldr.Lookup("runtime.mapinitnoop", abiInternalVer)
   128  	if d.mapinitnoop == 0 {
   129  		panic("could not look up runtime.mapinitnoop")
   130  	}
   131  	if d.ctxt.mainInittasks != 0 {
   132  		d.mark(d.ctxt.mainInittasks, 0)
   133  	}
   134  }
   135  
   136  func (d *deadcodePass) flood() {
   137  	var methods []methodref
   138  	for !d.wq.empty() {
   139  		symIdx := d.wq.pop()
   140  
   141  		
   142  		
   143  		d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
   144  
   145  		isgotype := d.ldr.IsGoType(symIdx)
   146  		relocs := d.ldr.Relocs(symIdx)
   147  		var usedInIface bool
   148  
   149  		if isgotype {
   150  			if d.dynlink {
   151  				
   152  				
   153  				d.ldr.SetAttrUsedInIface(symIdx, true)
   154  			}
   155  			usedInIface = d.ldr.AttrUsedInIface(symIdx)
   156  		}
   157  
   158  		methods = methods[:0]
   159  		for i := 0; i < relocs.Count(); i++ {
   160  			r := relocs.At(i)
   161  			if r.Weak() {
   162  				convertWeakToStrong := false
   163  				
   164  				
   165  				
   166  				if d.ctxt.linkShared && d.ldr.IsItab(symIdx) {
   167  					convertWeakToStrong = true
   168  				}
   169  				
   170  				
   171  				
   172  				
   173  				
   174  				if d.ctxt.canUsePlugins && r.Type().IsDirectCall() {
   175  					convertWeakToStrong = true
   176  				}
   177  				if !convertWeakToStrong {
   178  					
   179  					continue
   180  				}
   181  			}
   182  			t := r.Type()
   183  			switch t {
   184  			case objabi.R_METHODOFF:
   185  				if i+2 >= relocs.Count() {
   186  					panic("expect three consecutive R_METHODOFF relocs")
   187  				}
   188  				if usedInIface {
   189  					methods = append(methods, methodref{src: symIdx, r: i})
   190  					
   191  					
   192  					
   193  					
   194  					
   195  					rs := r.Sym()
   196  					if !d.ldr.AttrUsedInIface(rs) {
   197  						d.ldr.SetAttrUsedInIface(rs, true)
   198  						if d.ldr.AttrReachable(rs) {
   199  							d.ldr.SetAttrReachable(rs, false)
   200  							d.mark(rs, symIdx)
   201  						}
   202  					}
   203  				}
   204  				i += 2
   205  				continue
   206  			case objabi.R_USETYPE:
   207  				
   208  				
   209  				
   210  				continue
   211  			case objabi.R_USEIFACE:
   212  				
   213  				
   214  				
   215  				rs := r.Sym()
   216  				if d.ldr.IsItab(rs) {
   217  					
   218  					
   219  					rs = decodeItabType(d.ldr, d.ctxt.Arch, rs)
   220  				}
   221  				if !d.ldr.IsGoType(rs) && !d.ctxt.linkShared {
   222  					panic(fmt.Sprintf("R_USEIFACE in %s references %s which is not a type or itab", d.ldr.SymName(symIdx), d.ldr.SymName(rs)))
   223  				}
   224  				if !d.ldr.AttrUsedInIface(rs) {
   225  					d.ldr.SetAttrUsedInIface(rs, true)
   226  					if d.ldr.AttrReachable(rs) {
   227  						d.ldr.SetAttrReachable(rs, false)
   228  						d.mark(rs, symIdx)
   229  					}
   230  				}
   231  				continue
   232  			case objabi.R_USEIFACEMETHOD:
   233  				
   234  				
   235  				rs := r.Sym()
   236  				if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) {
   237  					
   238  					
   239  					
   240  					continue
   241  				}
   242  				m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add())
   243  				if d.ctxt.Debugvlog > 1 {
   244  					d.ctxt.Logf("reached iface method: %v\n", m)
   245  				}
   246  				d.ifaceMethod[m] = true
   247  				continue
   248  			case objabi.R_USENAMEDMETHOD:
   249  				name := d.decodeGenericIfaceMethod(d.ldr, r.Sym())
   250  				if d.ctxt.Debugvlog > 1 {
   251  					d.ctxt.Logf("reached generic iface method: %s\n", name)
   252  				}
   253  				d.genericIfaceMethod[name] = true
   254  				continue 
   255  			case objabi.R_INITORDER:
   256  				
   257  				
   258  				
   259  				continue
   260  			}
   261  			rs := r.Sym()
   262  			if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
   263  				
   264  				
   265  				
   266  				
   267  				
   268  				
   269  				
   270  				
   271  				
   272  				
   273  				
   274  				d.ldr.SetAttrUsedInIface(rs, true)
   275  				d.ldr.SetAttrReachable(rs, false)
   276  			}
   277  			d.mark(rs, symIdx)
   278  		}
   279  		naux := d.ldr.NAux(symIdx)
   280  		for i := 0; i < naux; i++ {
   281  			a := d.ldr.Aux(symIdx, i)
   282  			if a.Type() == goobj.AuxGotype {
   283  				
   284  				
   285  				continue
   286  			}
   287  			d.mark(a.Sym(), symIdx)
   288  		}
   289  		
   290  		
   291  		if naux != 0 && d.ldr.IsPkgInit(symIdx) {
   292  
   293  			d.pkginits = append(d.pkginits, symIdx)
   294  		}
   295  		
   296  		
   297  		
   298  		
   299  		
   300  		
   301  		if d.ldr.IsExternal(symIdx) {
   302  			d.mark(d.ldr.OuterSym(symIdx), symIdx)
   303  			d.mark(d.ldr.SubSym(symIdx), symIdx)
   304  		}
   305  
   306  		if len(methods) != 0 {
   307  			if !isgotype {
   308  				panic("method found on non-type symbol")
   309  			}
   310  			
   311  			
   312  			
   313  			methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs)
   314  			if len(methods) != len(methodsigs) {
   315  				panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
   316  			}
   317  			for i, m := range methodsigs {
   318  				methods[i].m = m
   319  				if d.ctxt.Debugvlog > 1 {
   320  					d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx))
   321  				}
   322  			}
   323  			d.markableMethods = append(d.markableMethods, methods...)
   324  		}
   325  	}
   326  }
   327  
   328  
   329  
   330  
   331  func (d *deadcodePass) mapinitcleanup() {
   332  	for _, idx := range d.pkginits {
   333  		relocs := d.ldr.Relocs(idx)
   334  		var su *loader.SymbolBuilder
   335  		for i := 0; i < relocs.Count(); i++ {
   336  			r := relocs.At(i)
   337  			rs := r.Sym()
   338  			if r.Weak() && r.Type().IsDirectCall() && !d.ldr.AttrReachable(rs) {
   339  				
   340  				rsn := d.ldr.SymName(rs)
   341  				if !strings.Contains(rsn, "map.init") {
   342  					panic(fmt.Sprintf("internal error: expected map.init sym for weak call reloc, got %s -> %s", d.ldr.SymName(idx), rsn))
   343  				}
   344  				d.ldr.SetAttrReachable(d.mapinitnoop, true)
   345  				if d.ctxt.Debugvlog > 1 {
   346  					d.ctxt.Logf("deadcode: %s rewrite %s ref to %s\n",
   347  						d.ldr.SymName(idx), rsn,
   348  						d.ldr.SymName(d.mapinitnoop))
   349  				}
   350  				if su == nil {
   351  					su = d.ldr.MakeSymbolUpdater(idx)
   352  				}
   353  				su.SetRelocSym(i, d.mapinitnoop)
   354  			}
   355  		}
   356  	}
   357  }
   358  
   359  func (d *deadcodePass) mark(symIdx, parent loader.Sym) {
   360  	if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
   361  		d.wq.push(symIdx)
   362  		d.ldr.SetAttrReachable(symIdx, true)
   363  		if buildcfg.Experiment.FieldTrack && d.ldr.Reachparent[symIdx] == 0 {
   364  			d.ldr.Reachparent[symIdx] = parent
   365  		}
   366  		if *flagDumpDep {
   367  			to := d.ldr.SymName(symIdx)
   368  			if to != "" {
   369  				to = d.dumpDepAddFlags(to, symIdx)
   370  				from := "_"
   371  				if parent != 0 {
   372  					from = d.ldr.SymName(parent)
   373  					from = d.dumpDepAddFlags(from, parent)
   374  				}
   375  				fmt.Printf("%s -> %s\n", from, to)
   376  			}
   377  		}
   378  	}
   379  }
   380  
   381  func (d *deadcodePass) dumpDepAddFlags(name string, symIdx loader.Sym) string {
   382  	var flags strings.Builder
   383  	if d.ldr.AttrUsedInIface(symIdx) {
   384  		flags.WriteString("<UsedInIface>")
   385  	}
   386  	if d.ldr.IsReflectMethod(symIdx) {
   387  		flags.WriteString("<ReflectMethod>")
   388  	}
   389  	if flags.Len() > 0 {
   390  		return name + " " + flags.String()
   391  	}
   392  	return name
   393  }
   394  
   395  func (d *deadcodePass) markMethod(m methodref) {
   396  	relocs := d.ldr.Relocs(m.src)
   397  	d.mark(relocs.At(m.r).Sym(), m.src)
   398  	d.mark(relocs.At(m.r+1).Sym(), m.src)
   399  	d.mark(relocs.At(m.r+2).Sym(), m.src)
   400  }
   401  
   402  
   403  
   404  
   405  
   406  
   407  
   408  
   409  
   410  
   411  
   412  
   413  
   414  
   415  
   416  
   417  
   418  
   419  
   420  
   421  
   422  
   423  
   424  
   425  
   426  
   427  
   428  
   429  
   430  
   431  
   432  
   433  
   434  
   435  
   436  
   437  
   438  
   439  
   440  
   441  
   442  
   443  func deadcode(ctxt *Link) {
   444  	ldr := ctxt.loader
   445  	d := deadcodePass{ctxt: ctxt, ldr: ldr}
   446  	d.init()
   447  	d.flood()
   448  
   449  	if ctxt.DynlinkingGo() {
   450  		
   451  		
   452  		d.reflectSeen = true
   453  	}
   454  
   455  	for {
   456  		
   457  		
   458  		
   459  		
   460  		rem := d.markableMethods[:0]
   461  		for _, m := range d.markableMethods {
   462  			if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
   463  				d.markMethod(m)
   464  			} else {
   465  				rem = append(rem, m)
   466  			}
   467  		}
   468  		d.markableMethods = rem
   469  
   470  		if d.wq.empty() {
   471  			
   472  			break
   473  		}
   474  		d.flood()
   475  	}
   476  	if *flagPruneWeakMap {
   477  		d.mapinitcleanup()
   478  	}
   479  }
   480  
   481  
   482  type methodsig struct {
   483  	name string
   484  	typ  loader.Sym 
   485  }
   486  
   487  
   488  
   489  
   490  type methodref struct {
   491  	m   methodsig
   492  	src loader.Sym 
   493  	r   int        
   494  }
   495  
   496  func (m methodref) isExported() bool {
   497  	for _, r := range m.m.name {
   498  		return unicode.IsUpper(r)
   499  	}
   500  	panic("methodref has no signature")
   501  }
   502  
   503  
   504  
   505  
   506  
   507  
   508  
   509  func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
   510  	if cap(d.methodsigstmp) < count {
   511  		d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...)
   512  	}
   513  	var methods = d.methodsigstmp[:count]
   514  	for i := 0; i < count; i++ {
   515  		methods[i].name = decodetypeName(ldr, symIdx, relocs, off)
   516  		methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4))
   517  		off += size
   518  	}
   519  	return methods
   520  }
   521  
   522  
   523  func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig {
   524  	p := ldr.Data(symIdx)
   525  	if p == nil {
   526  		panic(fmt.Sprintf("missing symbol %q", ldr.SymName(symIdx)))
   527  	}
   528  	if decodetypeKind(arch, p) != abi.Interface {
   529  		panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
   530  	}
   531  	relocs := ldr.Relocs(symIdx)
   532  	var m methodsig
   533  	m.name = decodetypeName(ldr, symIdx, &relocs, int(off))
   534  	m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4))
   535  	return m
   536  }
   537  
   538  
   539  func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string {
   540  	return ldr.DataString(symIdx)
   541  }
   542  
   543  func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
   544  	p := ldr.Data(symIdx)
   545  	if !decodetypeHasUncommon(arch, p) {
   546  		panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
   547  	}
   548  	off := commonsize(arch) 
   549  	switch decodetypeKind(arch, p) {
   550  	case abi.Struct: 
   551  		off += 4 * arch.PtrSize
   552  	case abi.Pointer: 
   553  		off += arch.PtrSize
   554  	case abi.Func: 
   555  		off += arch.PtrSize 
   556  	case abi.Slice: 
   557  		off += arch.PtrSize
   558  	case abi.Array: 
   559  		off += 3 * arch.PtrSize
   560  	case abi.Chan: 
   561  		off += 2 * arch.PtrSize
   562  	case abi.Map:
   563  		if buildcfg.Experiment.SwissMap {
   564  			off += 7*arch.PtrSize + 4 
   565  			if arch.PtrSize == 8 {
   566  				off += 4 
   567  			}
   568  		} else {
   569  			off += 4*arch.PtrSize + 8 
   570  		}
   571  	case abi.Interface: 
   572  		off += 3 * arch.PtrSize
   573  	default:
   574  		
   575  	}
   576  
   577  	mcount := int(decodeInuxi(arch, p[off+4:], 2))
   578  	moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
   579  	off += moff                
   580  	const sizeofMethod = 4 * 4 
   581  	return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
   582  }
   583  
View as plain text