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/buildcfg"
15 "unicode"
16 )
17
18 var _ = fmt.Print
19
20 type deadcodePass struct {
21 ctxt *Link
22 ldr *loader.Loader
23 wq heap
24
25 ifaceMethod map[methodsig]bool
26 markableMethods []methodref
27 reflectSeen bool
28 dynlink bool
29
30 methodsigstmp []methodsig
31 }
32
33 func (d *deadcodePass) init() {
34 d.ldr.InitReachable()
35 d.ifaceMethod = make(map[methodsig]bool)
36 if buildcfg.Experiment.FieldTrack {
37 d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
38 }
39 d.dynlink = d.ctxt.DynlinkingGo()
40
41 if d.ctxt.BuildMode == BuildModeShared {
42
43
44 n := d.ldr.NDef()
45 for i := 1; i < n; i++ {
46 s := loader.Sym(i)
47 d.mark(s, 0)
48 }
49 return
50 }
51
52 var names []string
53
54
55
56 if d.ctxt.linkShared && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
57 names = append(names, "main.main", "main..inittask")
58 } else {
59
60 if d.ctxt.LinkMode == LinkExternal && (d.ctxt.BuildMode == BuildModeExe || d.ctxt.BuildMode == BuildModePIE) {
61 if d.ctxt.HeadType == objabi.Hwindows && d.ctxt.Arch.Family == sys.I386 {
62 *flagEntrySymbol = "_main"
63 } else {
64 *flagEntrySymbol = "main"
65 }
66 }
67 names = append(names, *flagEntrySymbol)
68 }
69
70
71 names = append(names, "runtime.unreachableMethod")
72 if !d.ctxt.linkShared && d.ctxt.BuildMode != BuildModePlugin {
73
74
75
76 names = append(names, "runtime.buildVersion", "runtime.modinfo")
77 }
78 if d.ctxt.BuildMode == BuildModePlugin {
79 names = append(names, objabi.PathToPrefix(*flagPluginPath)+"..inittask", objabi.PathToPrefix(*flagPluginPath)+".main", "go.plugin.tabs")
80
81
82
83 exportsIdx := d.ldr.Lookup("go.plugin.exports", 0)
84 if exportsIdx != 0 {
85 relocs := d.ldr.Relocs(exportsIdx)
86 for i := 0; i < relocs.Count(); i++ {
87 d.mark(relocs.At(i).Sym(), 0)
88 }
89 }
90 }
91
92 if d.ctxt.Debugvlog > 1 {
93 d.ctxt.Logf("deadcode start names: %v\n", names)
94 }
95
96 for _, name := range names {
97
98 d.mark(d.ldr.Lookup(name, 0), 0)
99
100 d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0)
101 }
102
103
104 for _, s := range d.ctxt.dynexp {
105 if d.ctxt.Debugvlog > 1 {
106 d.ctxt.Logf("deadcode start dynexp: %s<%d>\n", d.ldr.SymName(s), d.ldr.SymVersion(s))
107 }
108 d.mark(s, 0)
109 }
110 }
111
112 func (d *deadcodePass) flood() {
113 var methods []methodref
114 for !d.wq.empty() {
115 symIdx := d.wq.pop()
116
117 d.reflectSeen = d.reflectSeen || d.ldr.IsReflectMethod(symIdx)
118
119 isgotype := d.ldr.IsGoType(symIdx)
120 relocs := d.ldr.Relocs(symIdx)
121 var usedInIface bool
122
123 if isgotype {
124 if d.dynlink {
125
126
127 d.ldr.SetAttrUsedInIface(symIdx, true)
128 }
129 usedInIface = d.ldr.AttrUsedInIface(symIdx)
130 }
131
132 methods = methods[:0]
133 for i := 0; i < relocs.Count(); i++ {
134 r := relocs.At(i)
135 if r.Weak() {
136 continue
137 }
138 t := r.Type()
139 switch t {
140 case objabi.R_METHODOFF:
141 if i+2 >= relocs.Count() {
142 panic("expect three consecutive R_METHODOFF relocs")
143 }
144 if usedInIface {
145 methods = append(methods, methodref{src: symIdx, r: i})
146
147
148
149
150
151 rs := r.Sym()
152 if !d.ldr.AttrUsedInIface(rs) {
153 d.ldr.SetAttrUsedInIface(rs, true)
154 if d.ldr.AttrReachable(rs) {
155 d.ldr.SetAttrReachable(rs, false)
156 d.mark(rs, symIdx)
157 }
158 }
159 }
160 i += 2
161 continue
162 case objabi.R_USETYPE:
163
164
165
166 continue
167 case objabi.R_USEIFACE:
168
169
170
171 rs := r.Sym()
172 if !d.ldr.AttrUsedInIface(rs) {
173 d.ldr.SetAttrUsedInIface(rs, true)
174 if d.ldr.AttrReachable(rs) {
175 d.ldr.SetAttrReachable(rs, false)
176 d.mark(rs, symIdx)
177 }
178 }
179 continue
180 case objabi.R_USEIFACEMETHOD:
181
182
183 rs := r.Sym()
184 if d.ctxt.linkShared && (d.ldr.SymType(rs) == sym.SDYNIMPORT || d.ldr.SymType(rs) == sym.Sxxx) {
185
186
187
188 continue
189 }
190 m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add())
191 if d.ctxt.Debugvlog > 1 {
192 d.ctxt.Logf("reached iface method: %v\n", m)
193 }
194 d.ifaceMethod[m] = true
195 continue
196 }
197 rs := r.Sym()
198 if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
199
200
201
202
203
204
205
206
207
208
209
210 d.ldr.SetAttrUsedInIface(rs, true)
211 d.ldr.SetAttrReachable(rs, false)
212 }
213 d.mark(rs, symIdx)
214 }
215 naux := d.ldr.NAux(symIdx)
216 for i := 0; i < naux; i++ {
217 a := d.ldr.Aux(symIdx, i)
218 if a.Type() == goobj.AuxGotype {
219
220
221 continue
222 }
223 d.mark(a.Sym(), symIdx)
224 }
225
226
227
228
229
230
231 if d.ldr.IsExternal(symIdx) {
232 d.mark(d.ldr.OuterSym(symIdx), symIdx)
233 d.mark(d.ldr.SubSym(symIdx), symIdx)
234 }
235
236 if len(methods) != 0 {
237 if !isgotype {
238 panic("method found on non-type symbol")
239 }
240
241
242
243 methodsigs := d.decodetypeMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs)
244 if len(methods) != len(methodsigs) {
245 panic(fmt.Sprintf("%q has %d method relocations for %d methods", d.ldr.SymName(symIdx), len(methods), len(methodsigs)))
246 }
247 for i, m := range methodsigs {
248 methods[i].m = m
249 if d.ctxt.Debugvlog > 1 {
250 d.ctxt.Logf("markable method: %v of sym %v %s\n", m, symIdx, d.ldr.SymName(symIdx))
251 }
252 }
253 d.markableMethods = append(d.markableMethods, methods...)
254 }
255 }
256 }
257
258 func (d *deadcodePass) mark(symIdx, parent loader.Sym) {
259 if symIdx != 0 && !d.ldr.AttrReachable(symIdx) {
260 d.wq.push(symIdx)
261 d.ldr.SetAttrReachable(symIdx, true)
262 if buildcfg.Experiment.FieldTrack && d.ldr.Reachparent[symIdx] == 0 {
263 d.ldr.Reachparent[symIdx] = parent
264 }
265 if *flagDumpDep {
266 to := d.ldr.SymName(symIdx)
267 if to != "" {
268 if d.ldr.AttrUsedInIface(symIdx) {
269 to += " <UsedInIface>"
270 }
271 from := "_"
272 if parent != 0 {
273 from = d.ldr.SymName(parent)
274 if d.ldr.AttrUsedInIface(parent) {
275 from += " <UsedInIface>"
276 }
277 }
278 fmt.Printf("%s -> %s\n", from, to)
279 }
280 }
281 }
282 }
283
284 func (d *deadcodePass) markMethod(m methodref) {
285 relocs := d.ldr.Relocs(m.src)
286 d.mark(relocs.At(m.r).Sym(), m.src)
287 d.mark(relocs.At(m.r+1).Sym(), m.src)
288 d.mark(relocs.At(m.r+2).Sym(), m.src)
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324 func deadcode(ctxt *Link) {
325 ldr := ctxt.loader
326 d := deadcodePass{ctxt: ctxt, ldr: ldr}
327 d.init()
328 d.flood()
329
330 methSym := ldr.Lookup("reflect.Value.Method", sym.SymVerABIInternal)
331 methByNameSym := ldr.Lookup("reflect.Value.MethodByName", sym.SymVerABIInternal)
332
333 if ctxt.DynlinkingGo() {
334
335
336 d.reflectSeen = true
337 }
338
339 for {
340
341
342
343 d.reflectSeen = d.reflectSeen || (methSym != 0 && ldr.AttrReachable(methSym)) || (methByNameSym != 0 && ldr.AttrReachable(methByNameSym))
344
345
346
347
348
349 rem := d.markableMethods[:0]
350 for _, m := range d.markableMethods {
351 if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
352 d.markMethod(m)
353 } else {
354 rem = append(rem, m)
355 }
356 }
357 d.markableMethods = rem
358
359 if d.wq.empty() {
360
361 break
362 }
363 d.flood()
364 }
365 }
366
367
368 type methodsig struct {
369 name string
370 typ loader.Sym
371 }
372
373
374
375
376 type methodref struct {
377 m methodsig
378 src loader.Sym
379 r int
380 }
381
382 func (m methodref) isExported() bool {
383 for _, r := range m.m.name {
384 return unicode.IsUpper(r)
385 }
386 panic("methodref has no signature")
387 }
388
389
390
391
392
393
394
395 func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, off, size, count int) []methodsig {
396 if cap(d.methodsigstmp) < count {
397 d.methodsigstmp = append(d.methodsigstmp[:0], make([]methodsig, count)...)
398 }
399 var methods = d.methodsigstmp[:count]
400 for i := 0; i < count; i++ {
401 methods[i].name = decodetypeName(ldr, symIdx, relocs, off)
402 methods[i].typ = decodeRelocSym(ldr, symIdx, relocs, int32(off+4))
403 off += size
404 }
405 return methods
406 }
407
408
409 func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig {
410 p := ldr.Data(symIdx)
411 if decodetypeKind(arch, p)&kindMask != kindInterface {
412 panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx)))
413 }
414 relocs := ldr.Relocs(symIdx)
415 var m methodsig
416 m.name = decodetypeName(ldr, symIdx, &relocs, int(off))
417 m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4))
418 return m
419 }
420
421 func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
422 p := ldr.Data(symIdx)
423 if !decodetypeHasUncommon(arch, p) {
424 panic(fmt.Sprintf("no methods on %q", ldr.SymName(symIdx)))
425 }
426 off := commonsize(arch)
427 switch decodetypeKind(arch, p) & kindMask {
428 case kindStruct:
429 off += 4 * arch.PtrSize
430 case kindPtr:
431 off += arch.PtrSize
432 case kindFunc:
433 off += arch.PtrSize
434 case kindSlice:
435 off += arch.PtrSize
436 case kindArray:
437 off += 3 * arch.PtrSize
438 case kindChan:
439 off += 2 * arch.PtrSize
440 case kindMap:
441 off += 4*arch.PtrSize + 8
442 case kindInterface:
443 off += 3 * arch.PtrSize
444 default:
445
446 }
447
448 mcount := int(decodeInuxi(arch, p[off+4:], 2))
449 moff := int(decodeInuxi(arch, p[off+4+2+2:], 4))
450 off += moff
451 const sizeofMethod = 4 * 4
452 return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofMethod, mcount)
453 }
454
View as plain text