aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link/internal/ld/deadcode.go
blob: 13ddcdac24c4f482f6a1c08c98561d9d636283c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Copyright 2016 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/link/internal/sym"
)

// 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.Call, .Method, or reflect.Method.Func
//
// 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 to see if any of:
//	- reflect.Value.Call is reachable
//	- reflect.Value.Method is reachable
// 	- reflect.Type.Method or MethodByName is called.
// If any of these happen, all bets are off and all exported methods
// of reachable types are marked reachable.
//
// Any unreached text symbols are removed from ctxt.Textp.
func deadcode(ctxt *Link) {
	deadcode2(ctxt)
}

// addToTextp populates the context Textp slice (needed in various places
// in the linker) and also the unit Textp slices (needed by the "old"
// phase 2 DWARF generation).
func addToTextp(ctxt *Link) {

	// First set up ctxt.Textp, based on ctxt.Textp2.
	textp := make([]*sym.Symbol, 0, len(ctxt.Textp2))
	haveshlibs := len(ctxt.Shlibs) > 0
	for _, tsym := range ctxt.Textp2 {
		sp := ctxt.loader.Syms[tsym]
		if sp == nil || !ctxt.loader.AttrReachable(tsym) {
			panic("should never happen")
		}
		if haveshlibs && sp.Type == sym.SDYNIMPORT {
			continue
		}
		textp = append(textp, sp)
	}
	ctxt.Textp = textp

	// Dupok symbols may be defined in multiple packages; the
	// associated package for a dupok sym is chosen sort of
	// arbitrarily (the first containing package that the linker
	// loads). The loop below canonicalizes the File to the package
	// with which it will be laid down in text. Assumes that
	// ctxt.Library is already in postorder.
	for _, doInternal := range [2]bool{true, false} {
		for _, lib := range ctxt.Library {
			if isRuntimeDepPkg(lib.Pkg) != doInternal {
				continue
			}
			for _, dsym := range lib.DupTextSyms2 {
				tsp := ctxt.loader.Syms[dsym]
				if !tsp.Attr.OnList() {
					tsp.Attr |= sym.AttrOnList
					tsp.File = objabi.PathToPrefix(lib.Pkg)
				}
			}
		}
	}

	// Finally, set up compilation unit Textp slices. Can be removed
	// once loader-Sym DWARF-gen phase 2 is always enabled.
	for _, lib := range ctxt.Library {
		for _, unit := range lib.Units {
			for _, usym := range unit.Textp2 {
				usp := ctxt.loader.Syms[usym]
				usp.Attr |= sym.AttrOnList
				unit.Textp = append(unit.Textp, usp)
			}
		}
	}
}