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