Source file src/pkg/cmd/link/internal/ld/pcln.go
1
2
3
4
5 package ld
6
7 import (
8 "cmd/internal/objabi"
9 "cmd/internal/src"
10 "cmd/internal/sys"
11 "cmd/link/internal/sym"
12 "encoding/binary"
13 "log"
14 "os"
15 "path/filepath"
16 "strings"
17 )
18
19
20 type PCIter struct {
21 p []byte
22 pc uint32
23 nextpc uint32
24 pcscale uint32
25 value int32
26 start bool
27 done bool
28 }
29
30
31 func newPCIter(ctxt *Link) *PCIter {
32 it := new(PCIter)
33 it.pcscale = uint32(ctxt.Arch.MinLC)
34 return it
35 }
36
37
38 func (it *PCIter) next() {
39 it.pc = it.nextpc
40 if it.done {
41 return
42 }
43 if len(it.p) == 0 {
44 it.done = true
45 return
46 }
47
48
49 val, n := binary.Varint(it.p)
50 if n <= 0 {
51 log.Fatalf("bad value varint in pciternext: read %v", n)
52 }
53 it.p = it.p[n:]
54
55 if val == 0 && !it.start {
56 it.done = true
57 return
58 }
59
60 it.start = false
61 it.value += int32(val)
62
63
64 pc, n := binary.Uvarint(it.p)
65 if n <= 0 {
66 log.Fatalf("bad pc varint in pciternext: read %v", n)
67 }
68 it.p = it.p[n:]
69
70 it.nextpc = it.pc + uint32(pc)*it.pcscale
71 }
72
73
74
75 func (it *PCIter) init(p []byte) {
76 it.p = p
77 it.pc = 0
78 it.nextpc = 0
79 it.value = -1
80 it.start = true
81 it.done = false
82 it.next()
83 }
84
85 func ftabaddstring(ftab *sym.Symbol, s string) int32 {
86 start := len(ftab.P)
87 ftab.Grow(int64(start + len(s) + 1))
88 copy(ftab.P[start:], s)
89 return int32(start)
90 }
91
92
93 func numberfile(ctxt *Link, file *sym.Symbol) {
94 if file.Type != sym.SFILEPATH {
95 ctxt.Filesyms = append(ctxt.Filesyms, file)
96 file.Value = int64(len(ctxt.Filesyms))
97 file.Type = sym.SFILEPATH
98 path := file.Name[len(src.FileSymPrefix):]
99 file.Name = expandGoroot(path)
100 }
101 }
102
103 func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
104
105 for _, f := range files {
106 numberfile(ctxt, f)
107 }
108
109 buf := make([]byte, binary.MaxVarintLen32)
110 newval := int32(-1)
111 var out sym.Pcdata
112 it := newPCIter(ctxt)
113 for it.init(d.P); !it.done; it.next() {
114
115 oldval := it.value
116
117 var val int32
118 if oldval == -1 {
119 val = -1
120 } else {
121 if oldval < 0 || oldval >= int32(len(files)) {
122 log.Fatalf("bad pcdata %d", oldval)
123 }
124 val = int32(files[oldval].Value)
125 }
126
127 dv := val - newval
128 newval = val
129
130
131 n := binary.PutVarint(buf, int64(dv))
132 out.P = append(out.P, buf[:n]...)
133
134
135 pc := (it.nextpc - it.pc) / it.pcscale
136 n = binary.PutUvarint(buf, uint64(pc))
137 out.P = append(out.P, buf[:n]...)
138 }
139
140
141
142 out.P = append(out.P, 0)
143
144 *d = out
145 }
146
147
148 func onlycsymbol(s *sym.Symbol) bool {
149 switch s.Name {
150 case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
151 return true
152 }
153 if strings.HasPrefix(s.Name, "_cgoexp_") {
154 return true
155 }
156 return false
157 }
158
159 func emitPcln(ctxt *Link, s *sym.Symbol) bool {
160 if s == nil {
161 return true
162 }
163 if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) {
164 return false
165 }
166
167
168 return !s.Attr.Container()
169 }
170
171
172
173
174 var pclntabZpcln sym.FuncInfo
175
176
177 var pclntabNfunc int32
178 var pclntabFiletabOffset int32
179 var pclntabPclntabOffset int32
180 var pclntabFirstFunc *sym.Symbol
181 var pclntabLastFunc *sym.Symbol
182
183 func (ctxt *Link) pclntab() {
184 funcdataBytes := int64(0)
185 ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
186 ftab.Type = sym.SPCLNTAB
187 ftab.Attr |= sym.AttrReachable
188
189
190
191
192
193
194
195
196
197 for _, s := range ctxt.Textp {
198 if s.Outer != nil {
199 s.Outer.Attr |= sym.AttrContainer
200 }
201 }
202
203
204 var nfunc int32
205 for _, s := range ctxt.Textp {
206 if !emitPcln(ctxt, s) {
207 continue
208 }
209 nfunc++
210 if pclntabFirstFunc == nil {
211 pclntabFirstFunc = s
212 }
213 }
214
215 pclntabNfunc = nfunc
216 ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
217 ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb)
218 ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
219 ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
220 ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
221 pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
222
223 funcnameoff := make(map[string]int32)
224 nameToOffset := func(name string) int32 {
225 nameoff, ok := funcnameoff[name]
226 if !ok {
227 nameoff = ftabaddstring(ftab, name)
228 funcnameoff[name] = nameoff
229 }
230 return nameoff
231 }
232
233 pctaboff := make(map[string]uint32)
234 writepctab := func(off int32, p []byte) int32 {
235 start, ok := pctaboff[string(p)]
236 if !ok {
237 if len(p) > 0 {
238 start = uint32(len(ftab.P))
239 ftab.AddBytes(p)
240 }
241 pctaboff[string(p)] = start
242 }
243 newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
244 return newoff
245 }
246
247 nfunc = 0
248 for _, s := range ctxt.Textp {
249 if !emitPcln(ctxt, s) {
250 continue
251 }
252 pcln := s.FuncInfo
253 if pcln == nil {
254 pcln = &pclntabZpcln
255 }
256
257 if len(pcln.InlTree) > 0 {
258 if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex {
259
260 pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
261 copy(pcdata, pcln.Pcdata)
262 pcln.Pcdata = pcdata
263 }
264
265 if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree {
266
267 funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1)
268 funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
269 copy(funcdata, pcln.Funcdata)
270 copy(funcdataoff, pcln.Funcdataoff)
271 pcln.Funcdata = funcdata
272 pcln.Funcdataoff = funcdataoff
273 }
274 }
275
276 funcstart := int32(len(ftab.P))
277 funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
278
279 ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s)
280 ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
281
282
283
284
285
286 off := funcstart
287
288 end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize)
289 if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
290 end += 4
291 }
292 ftab.Grow(int64(end))
293
294
295 off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s))
296
297
298 nameoff := nameToOffset(s.Name)
299 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
300
301
302
303 args := uint32(0)
304 if s.FuncInfo != nil {
305 args = uint32(s.FuncInfo.Args)
306 }
307 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
308
309
310 deferreturn := uint32(0)
311 lastWasmAddr := uint32(0)
312 for _, r := range s.R {
313 if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR {
314
315
316
317
318
319 lastWasmAddr = uint32(r.Add)
320 }
321 if r.Type.IsDirectJump() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
322 if ctxt.Arch.Family == sys.Wasm {
323 deferreturn = lastWasmAddr
324 } else {
325
326
327
328
329 deferreturn = uint32(r.Off)
330 }
331 break
332 }
333 }
334 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
335
336 if pcln != &pclntabZpcln {
337 renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
338 if false {
339
340 it := newPCIter(ctxt)
341 for it.init(pcln.Pcfile.P); !it.done; it.next() {
342 if it.value < 1 || it.value > int32(len(ctxt.Filesyms)) {
343 Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(ctxt.Filesyms))
344 errorexit()
345 }
346 }
347 }
348 }
349
350 if len(pcln.InlTree) > 0 {
351 inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
352 inlTreeSym.Type = sym.SRODATA
353 inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK
354
355 for i, call := range pcln.InlTree {
356
357
358
359
360
361 numberfile(ctxt, call.File)
362 nameoff := nameToOffset(call.Func.Name)
363
364 inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
365 inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func.Name, call.Func.File)))
366
367 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
368 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
369 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
370 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
371 }
372
373 pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
374 pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline
375 }
376
377
378 off = writepctab(off, pcln.Pcsp.P)
379 off = writepctab(off, pcln.Pcfile.P)
380 off = writepctab(off, pcln.Pcline.P)
381 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
382
383
384 var file string
385 if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
386 file = s.FuncInfo.File[0].Name
387 }
388 funcID := objabi.GetFuncID(s.Name, file)
389
390 off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
391
392
393 off += 2
394
395
396 off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata))))
397 for i := range pcln.Pcdata {
398 off = writepctab(off, pcln.Pcdata[i].P)
399 }
400
401
402
403 if len(pcln.Funcdata) > 0 {
404 if off&int32(ctxt.Arch.PtrSize-1) != 0 {
405 off += 4
406 }
407 for i := range pcln.Funcdata {
408 dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
409 if pcln.Funcdata[i] == nil {
410 ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i]))
411 continue
412 }
413
414 funcdataBytes += pcln.Funcdata[i].Size
415 ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i])
416 }
417 off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize)
418 }
419
420 if off != end {
421 Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), ctxt.Arch.PtrSize)
422 errorexit()
423 }
424
425 nfunc++
426 }
427
428 last := ctxt.Textp[len(ctxt.Textp)-1]
429 pclntabLastFunc = last
430
431 ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size)
432
433
434 start := int32(len(ftab.P))
435
436 start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
437 pclntabFiletabOffset = start
438 ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
439
440 ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4)
441 ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1))
442 for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
443 s := ctxt.Filesyms[i]
444 ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
445 }
446
447 ftab.Size = int64(len(ftab.P))
448
449 if ctxt.Debugvlog != 0 {
450 ctxt.Logf("%5.2f pclntab=%d bytes, funcdata total %d bytes\n", Cputime(), ftab.Size, funcdataBytes)
451 }
452 }
453
454 func gorootFinal() string {
455 root := objabi.GOROOT
456 if final := os.Getenv("GOROOT_FINAL"); final != "" {
457 root = final
458 }
459 return root
460 }
461
462 func expandGoroot(s string) string {
463 const n = len("$GOROOT")
464 if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
465 return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:]))
466 }
467 return s
468 }
469
470 const (
471 BUCKETSIZE = 256 * MINFUNC
472 SUBBUCKETS = 16
473 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
474 NOIDX = 0x7fffffff
475 )
476
477
478
479 func (ctxt *Link) findfunctab() {
480 t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
481 t.Type = sym.SRODATA
482 t.Attr |= sym.AttrReachable
483 t.Attr |= sym.AttrLocal
484
485
486 min := ctxt.Textp[0].Value
487 lastp := ctxt.Textp[len(ctxt.Textp)-1]
488 max := lastp.Value + lastp.Size
489
490
491
492 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
493
494 indexes := make([]int32, n)
495 for i := int32(0); i < n; i++ {
496 indexes[i] = NOIDX
497 }
498 idx := int32(0)
499 for i, s := range ctxt.Textp {
500 if !emitPcln(ctxt, s) {
501 continue
502 }
503 p := s.Value
504 var e *sym.Symbol
505 i++
506 if i < len(ctxt.Textp) {
507 e = ctxt.Textp[i]
508 }
509 for !emitPcln(ctxt, e) && i < len(ctxt.Textp) {
510 e = ctxt.Textp[i]
511 i++
512 }
513 q := max
514 if e != nil {
515 q = e.Value
516 }
517
518
519 for ; p < q; p += SUBBUCKETSIZE {
520 i = int((p - min) / SUBBUCKETSIZE)
521 if indexes[i] > idx {
522 indexes[i] = idx
523 }
524 }
525
526 i = int((q - 1 - min) / SUBBUCKETSIZE)
527 if indexes[i] > idx {
528 indexes[i] = idx
529 }
530 idx++
531 }
532
533
534 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
535
536 t.Grow(4*int64(nbuckets) + int64(n))
537
538
539 for i := int32(0); i < nbuckets; i++ {
540 base := indexes[i*SUBBUCKETS]
541 if base == NOIDX {
542 Errorf(nil, "hole in findfunctab")
543 }
544 t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
545 for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
546 idx = indexes[i*SUBBUCKETS+j]
547 if idx == NOIDX {
548 Errorf(nil, "hole in findfunctab")
549 }
550 if idx-base >= 256 {
551 Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
552 }
553
554 t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
555 }
556 }
557 }
558
View as plain text