github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/cmd/link/internal/ld/pcln.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ld 6 7 import ( 8 "cmd/internal/objabi" 9 "cmd/internal/src" 10 "cmd/link/internal/sym" 11 "log" 12 "os" 13 "path/filepath" 14 ) 15 16 // iteration over encoded pcdata tables. 17 18 func getvarint(pp *[]byte) uint32 { 19 v := uint32(0) 20 p := *pp 21 for shift := 0; ; shift += 7 { 22 v |= uint32(p[0]&0x7F) << uint(shift) 23 tmp4 := p 24 p = p[1:] 25 if tmp4[0]&0x80 == 0 { 26 break 27 } 28 } 29 30 *pp = p 31 return v 32 } 33 34 func pciternext(it *Pciter) { 35 it.pc = it.nextpc 36 if it.done != 0 { 37 return 38 } 39 if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { 40 it.done = 1 41 return 42 } 43 44 // value delta 45 v := getvarint(&it.p) 46 47 if v == 0 && it.start == 0 { 48 it.done = 1 49 return 50 } 51 52 it.start = 0 53 dv := int32(v>>1) ^ (int32(v<<31) >> 31) 54 it.value += dv 55 56 // pc delta 57 v = getvarint(&it.p) 58 59 it.nextpc = it.pc + v*it.pcscale 60 } 61 62 func pciterinit(ctxt *Link, it *Pciter, d *sym.Pcdata) { 63 it.d = *d 64 it.p = it.d.P 65 it.pc = 0 66 it.nextpc = 0 67 it.value = -1 68 it.start = 1 69 it.done = 0 70 it.pcscale = uint32(ctxt.Arch.MinLC) 71 pciternext(it) 72 } 73 74 func addvarint(d *sym.Pcdata, val uint32) { 75 n := int32(0) 76 for v := val; v >= 0x80; v >>= 7 { 77 n++ 78 } 79 n++ 80 81 old := len(d.P) 82 for cap(d.P) < len(d.P)+int(n) { 83 d.P = append(d.P[:cap(d.P)], 0) 84 } 85 d.P = d.P[:old+int(n)] 86 87 p := d.P[old:] 88 var v uint32 89 for v = val; v >= 0x80; v >>= 7 { 90 p[0] = byte(v | 0x80) 91 p = p[1:] 92 } 93 p[0] = byte(v) 94 } 95 96 func addpctab(ctxt *Link, ftab *sym.Symbol, off int32, d *sym.Pcdata) int32 { 97 var start int32 98 if len(d.P) > 0 { 99 start = int32(len(ftab.P)) 100 ftab.AddBytes(d.P) 101 } 102 return int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(start))) 103 } 104 105 func ftabaddstring(ctxt *Link, ftab *sym.Symbol, s string) int32 { 106 n := int32(len(s)) + 1 107 start := int32(len(ftab.P)) 108 ftab.Grow(int64(start) + int64(n) + 1) 109 copy(ftab.P[start:], s) 110 return start 111 } 112 113 // numberfile assigns a file number to the file if it hasn't been assigned already. 114 func numberfile(ctxt *Link, file *sym.Symbol) { 115 if file.Type != sym.SFILEPATH { 116 ctxt.Filesyms = append(ctxt.Filesyms, file) 117 file.Value = int64(len(ctxt.Filesyms)) 118 file.Type = sym.SFILEPATH 119 path := file.Name[len(src.FileSymPrefix):] 120 file.Name = expandGoroot(path) 121 } 122 } 123 124 func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { 125 var f *sym.Symbol 126 127 // Give files numbers. 128 for i := 0; i < len(files); i++ { 129 f = files[i] 130 numberfile(ctxt, f) 131 } 132 133 newval := int32(-1) 134 var out sym.Pcdata 135 var it Pciter 136 for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { 137 // value delta 138 oldval := it.value 139 140 var val int32 141 if oldval == -1 { 142 val = -1 143 } else { 144 if oldval < 0 || oldval >= int32(len(files)) { 145 log.Fatalf("bad pcdata %d", oldval) 146 } 147 val = int32(files[oldval].Value) 148 } 149 150 dv := val - newval 151 newval = val 152 v := (uint32(dv) << 1) ^ uint32(dv>>31) 153 addvarint(&out, v) 154 155 // pc delta 156 addvarint(&out, (it.nextpc-it.pc)/it.pcscale) 157 } 158 159 // terminating value delta 160 addvarint(&out, 0) 161 162 *d = out 163 } 164 165 // onlycsymbol reports whether this is a cgo symbol provided by the 166 // runtime and only used from C code. 167 func onlycsymbol(s *sym.Symbol) bool { 168 switch s.Name { 169 case "_cgo_topofstack", "_cgo_panic", "crosscall2": 170 return true 171 } 172 return false 173 } 174 175 func emitPcln(ctxt *Link, s *sym.Symbol) bool { 176 if s == nil { 177 return true 178 } 179 if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) { 180 return false 181 } 182 // We want to generate func table entries only for the "lowest level" symbols, 183 // not containers of subsymbols. 184 if s.Attr.Container() { 185 return true 186 } 187 return true 188 } 189 190 // pclntab initializes the pclntab symbol with 191 // runtime function and file name information. 192 193 var pclntabZpcln sym.FuncInfo 194 195 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. 196 var pclntabNfunc int32 197 var pclntabFiletabOffset int32 198 var pclntabPclntabOffset int32 199 var pclntabFirstFunc *sym.Symbol 200 var pclntabLastFunc *sym.Symbol 201 202 func (ctxt *Link) pclntab() { 203 funcdataBytes := int64(0) 204 ftab := ctxt.Syms.Lookup("runtime.pclntab", 0) 205 ftab.Type = sym.SPCLNTAB 206 ftab.Attr |= sym.AttrReachable 207 208 // See golang.org/s/go12symtab for the format. Briefly: 209 // 8-byte header 210 // nfunc [thearch.ptrsize bytes] 211 // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] 212 // end PC [thearch.ptrsize bytes] 213 // offset to file table [4 bytes] 214 nfunc := int32(0) 215 216 // Find container symbols and mark them as such. 217 for _, s := range ctxt.Textp { 218 if s.Outer != nil { 219 s.Outer.Attr |= sym.AttrContainer 220 } 221 } 222 223 for _, s := range ctxt.Textp { 224 if emitPcln(ctxt, s) { 225 nfunc++ 226 } 227 } 228 229 pclntabNfunc = nfunc 230 ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) 231 ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb) 232 ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) 233 ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) 234 ftab.SetUint(ctxt.Arch, 8, uint64(nfunc)) 235 pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize) 236 237 funcnameoff := make(map[string]int32) 238 nameToOffset := func(name string) int32 { 239 nameoff, ok := funcnameoff[name] 240 if !ok { 241 nameoff = ftabaddstring(ctxt, ftab, name) 242 funcnameoff[name] = nameoff 243 } 244 return nameoff 245 } 246 247 nfunc = 0 248 var last *sym.Symbol 249 for _, s := range ctxt.Textp { 250 last = s 251 if !emitPcln(ctxt, s) { 252 continue 253 } 254 pcln := s.FuncInfo 255 if pcln == nil { 256 pcln = &pclntabZpcln 257 } 258 259 if pclntabFirstFunc == nil { 260 pclntabFirstFunc = s 261 } 262 263 if len(pcln.InlTree) > 0 { 264 if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex { 265 // Create inlining pcdata table. 266 pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) 267 copy(pcdata, pcln.Pcdata) 268 pcln.Pcdata = pcdata 269 } 270 271 if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree { 272 // Create inline tree funcdata. 273 funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1) 274 funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) 275 copy(funcdata, pcln.Funcdata) 276 copy(funcdataoff, pcln.Funcdataoff) 277 pcln.Funcdata = funcdata 278 pcln.Funcdataoff = funcdataoff 279 } 280 } 281 282 funcstart := int32(len(ftab.P)) 283 funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) 284 285 ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s) 286 ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart)) 287 288 // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func 289 // and package debug/gosym. 290 291 // fixed size of struct, checked below 292 off := funcstart 293 294 end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize) 295 if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { 296 end += 4 297 } 298 ftab.Grow(int64(end)) 299 300 // entry uintptr 301 off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s)) 302 303 // name int32 304 nameoff := nameToOffset(s.Name) 305 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) 306 307 // args int32 308 // TODO: Move into funcinfo. 309 args := uint32(0) 310 if s.FuncInfo != nil { 311 args = uint32(s.FuncInfo.Args) 312 } 313 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) 314 315 // funcID uint32 316 funcID := objabi.FuncID_normal 317 switch s.Name { 318 case "runtime.goexit": 319 funcID = objabi.FuncID_goexit 320 case "runtime.jmpdefer": 321 funcID = objabi.FuncID_jmpdefer 322 case "runtime.mcall": 323 funcID = objabi.FuncID_mcall 324 case "runtime.morestack": 325 funcID = objabi.FuncID_morestack 326 case "runtime.mstart": 327 funcID = objabi.FuncID_mstart 328 case "runtime.rt0_go": 329 funcID = objabi.FuncID_rt0_go 330 case "runtime.asmcgocall": 331 funcID = objabi.FuncID_asmcgocall 332 case "runtime.sigpanic": 333 funcID = objabi.FuncID_sigpanic 334 case "runtime.runfinq": 335 funcID = objabi.FuncID_runfinq 336 case "runtime.bgsweep": 337 funcID = objabi.FuncID_bgsweep 338 case "runtime.forcegchelper": 339 funcID = objabi.FuncID_forcegchelper 340 case "runtime.timerproc": 341 funcID = objabi.FuncID_timerproc 342 case "runtime.gcBgMarkWorker": 343 funcID = objabi.FuncID_gcBgMarkWorker 344 case "runtime.systemstack_switch": 345 funcID = objabi.FuncID_systemstack_switch 346 case "runtime.systemstack": 347 funcID = objabi.FuncID_systemstack 348 case "runtime.cgocallback_gofunc": 349 funcID = objabi.FuncID_cgocallback_gofunc 350 case "runtime.gogo": 351 funcID = objabi.FuncID_gogo 352 case "runtime.externalthreadhandler": 353 funcID = objabi.FuncID_externalthreadhandler 354 } 355 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(funcID))) 356 357 if pcln != &pclntabZpcln { 358 renumberfiles(ctxt, pcln.File, &pcln.Pcfile) 359 if false { 360 // Sanity check the new numbering 361 var it Pciter 362 for pciterinit(ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { 363 if it.value < 1 || it.value > int32(len(ctxt.Filesyms)) { 364 Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(ctxt.Filesyms)) 365 errorexit() 366 } 367 } 368 } 369 } 370 371 if len(pcln.InlTree) > 0 { 372 inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0) 373 inlTreeSym.Type = sym.SRODATA 374 inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK 375 376 for i, call := range pcln.InlTree { 377 // Usually, call.File is already numbered since the file 378 // shows up in the Pcfile table. However, two inlined calls 379 // might overlap exactly so that only the innermost file 380 // appears in the Pcfile table. In that case, this assigns 381 // the outer file a number. 382 numberfile(ctxt, call.File) 383 nameoff := nameToOffset(call.Func.Name) 384 385 inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+0), uint32(call.Parent)) 386 inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+4), uint32(call.File.Value)) 387 inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+8), uint32(call.Line)) 388 inlTreeSym.SetUint32(ctxt.Arch, int64(i*16+12), uint32(nameoff)) 389 } 390 391 pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym 392 pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline 393 } 394 395 // pcdata 396 off = addpctab(ctxt, ftab, off, &pcln.Pcsp) 397 398 off = addpctab(ctxt, ftab, off, &pcln.Pcfile) 399 off = addpctab(ctxt, ftab, off, &pcln.Pcline) 400 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata)))) 401 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Funcdata)))) 402 for i := 0; i < len(pcln.Pcdata); i++ { 403 off = addpctab(ctxt, ftab, off, &pcln.Pcdata[i]) 404 } 405 406 // funcdata, must be pointer-aligned and we're only int32-aligned. 407 // Missing funcdata will be 0 (nil pointer). 408 if len(pcln.Funcdata) > 0 { 409 if off&int32(ctxt.Arch.PtrSize-1) != 0 { 410 off += 4 411 } 412 for i := 0; i < len(pcln.Funcdata); i++ { 413 if pcln.Funcdata[i] == nil { 414 ftab.SetUint(ctxt.Arch, int64(off)+int64(ctxt.Arch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i])) 415 } else { 416 // TODO: Dedup. 417 funcdataBytes += pcln.Funcdata[i].Size 418 419 ftab.SetAddrPlus(ctxt.Arch, int64(off)+int64(ctxt.Arch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) 420 } 421 } 422 423 off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize) 424 } 425 426 if off != end { 427 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) 428 errorexit() 429 } 430 431 nfunc++ 432 } 433 434 pclntabLastFunc = last 435 // Final entry of table is just end pc. 436 ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size) 437 438 // Start file table. 439 start := int32(len(ftab.P)) 440 441 start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) 442 pclntabFiletabOffset = start 443 ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) 444 445 ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4) 446 ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1)) 447 for i := len(ctxt.Filesyms) - 1; i >= 0; i-- { 448 s := ctxt.Filesyms[i] 449 ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ctxt, ftab, s.Name))) 450 } 451 452 ftab.Size = int64(len(ftab.P)) 453 454 if ctxt.Debugvlog != 0 { 455 ctxt.Logf("%5.2f pclntab=%d bytes, funcdata total %d bytes\n", Cputime(), ftab.Size, funcdataBytes) 456 } 457 } 458 459 func gorootFinal() string { 460 root := objabi.GOROOT 461 if final := os.Getenv("GOROOT_FINAL"); final != "" { 462 root = final 463 } 464 return root 465 } 466 467 func expandGoroot(s string) string { 468 const n = len("$GOROOT") 469 if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { 470 return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:])) 471 } 472 return s 473 } 474 475 const ( 476 BUCKETSIZE = 256 * MINFUNC 477 SUBBUCKETS = 16 478 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS 479 NOIDX = 0x7fffffff 480 ) 481 482 // findfunctab generates a lookup table to quickly find the containing 483 // function for a pc. See src/runtime/symtab.go:findfunc for details. 484 func (ctxt *Link) findfunctab() { 485 t := ctxt.Syms.Lookup("runtime.findfunctab", 0) 486 t.Type = sym.SRODATA 487 t.Attr |= sym.AttrReachable 488 t.Attr |= sym.AttrLocal 489 490 // find min and max address 491 min := ctxt.Textp[0].Value 492 max := int64(0) 493 for _, s := range ctxt.Textp { 494 max = s.Value + s.Size 495 } 496 497 // for each subbucket, compute the minimum of all symbol indexes 498 // that map to that subbucket. 499 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) 500 501 indexes := make([]int32, n) 502 for i := int32(0); i < n; i++ { 503 indexes[i] = NOIDX 504 } 505 idx := int32(0) 506 for i, s := range ctxt.Textp { 507 if !emitPcln(ctxt, s) { 508 continue 509 } 510 p := s.Value 511 var e *sym.Symbol 512 i++ 513 if i < len(ctxt.Textp) { 514 e = ctxt.Textp[i] 515 } 516 for !emitPcln(ctxt, e) && i < len(ctxt.Textp) { 517 e = ctxt.Textp[i] 518 i++ 519 } 520 q := max 521 if e != nil { 522 q = e.Value 523 } 524 525 //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); 526 for ; p < q; p += SUBBUCKETSIZE { 527 i = int((p - min) / SUBBUCKETSIZE) 528 if indexes[i] > idx { 529 indexes[i] = idx 530 } 531 } 532 533 i = int((q - 1 - min) / SUBBUCKETSIZE) 534 if indexes[i] > idx { 535 indexes[i] = idx 536 } 537 idx++ 538 } 539 540 // allocate table 541 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) 542 543 t.Grow(4*int64(nbuckets) + int64(n)) 544 545 // fill in table 546 for i := int32(0); i < nbuckets; i++ { 547 base := indexes[i*SUBBUCKETS] 548 if base == NOIDX { 549 Errorf(nil, "hole in findfunctab") 550 } 551 t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) 552 for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { 553 idx = indexes[i*SUBBUCKETS+j] 554 if idx == NOIDX { 555 Errorf(nil, "hole in findfunctab") 556 } 557 if idx-base >= 256 { 558 Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) 559 } 560 561 t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) 562 } 563 } 564 }