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