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