github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 "github.com/gagliardetto/golang-go/cmd/internal/obj" 9 "github.com/gagliardetto/golang-go/cmd/internal/objabi" 10 "github.com/gagliardetto/golang-go/cmd/internal/src" 11 "github.com/gagliardetto/golang-go/cmd/internal/sys" 12 "github.com/gagliardetto/golang-go/cmd/link/internal/sym" 13 "encoding/binary" 14 "fmt" 15 "log" 16 "os" 17 "path/filepath" 18 "strings" 19 ) 20 21 func ftabaddstring(ftab *sym.Symbol, s string) int32 { 22 start := len(ftab.P) 23 ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL 24 copy(ftab.P[start:], s) 25 return int32(start) 26 } 27 28 // numberfile assigns a file number to the file if it hasn't been assigned already. 29 func numberfile(ctxt *Link, file *sym.Symbol) { 30 if file.Type != sym.SFILEPATH { 31 ctxt.Filesyms = append(ctxt.Filesyms, file) 32 file.Value = int64(len(ctxt.Filesyms)) 33 file.Type = sym.SFILEPATH 34 path := file.Name[len(src.FileSymPrefix):] 35 file.Name = expandGoroot(path) 36 } 37 } 38 39 func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) { 40 // Give files numbers. 41 for _, f := range files { 42 numberfile(ctxt, f) 43 } 44 45 buf := make([]byte, binary.MaxVarintLen32) 46 newval := int32(-1) 47 var out sym.Pcdata 48 it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) 49 for it.Init(d.P); !it.Done; it.Next() { 50 // value delta 51 oldval := it.Value 52 53 var val int32 54 if oldval == -1 { 55 val = -1 56 } else { 57 if oldval < 0 || oldval >= int32(len(files)) { 58 log.Fatalf("bad pcdata %d", oldval) 59 } 60 val = int32(files[oldval].Value) 61 } 62 63 dv := val - newval 64 newval = val 65 66 // value 67 n := binary.PutVarint(buf, int64(dv)) 68 out.P = append(out.P, buf[:n]...) 69 70 // pc delta 71 pc := (it.NextPC - it.PC) / it.PCScale 72 n = binary.PutUvarint(buf, uint64(pc)) 73 out.P = append(out.P, buf[:n]...) 74 } 75 76 // terminating value delta 77 // we want to write varint-encoded 0, which is just 0 78 out.P = append(out.P, 0) 79 80 *d = out 81 } 82 83 // onlycsymbol reports whether this is a symbol that is referenced by C code. 84 func onlycsymbol(s *sym.Symbol) bool { 85 switch s.Name { 86 case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2": 87 return true 88 } 89 if strings.HasPrefix(s.Name, "_cgoexp_") { 90 return true 91 } 92 return false 93 } 94 95 func emitPcln(ctxt *Link, s *sym.Symbol) bool { 96 if s == nil { 97 return true 98 } 99 if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) { 100 return false 101 } 102 // We want to generate func table entries only for the "lowest level" symbols, 103 // not containers of subsymbols. 104 return !s.Attr.Container() 105 } 106 107 // pclntab initializes the pclntab symbol with 108 // runtime function and file name information. 109 110 var pclntabZpcln sym.FuncInfo 111 112 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. 113 var pclntabNfunc int32 114 var pclntabFiletabOffset int32 115 var pclntabPclntabOffset int32 116 var pclntabFirstFunc *sym.Symbol 117 var pclntabLastFunc *sym.Symbol 118 119 func (ctxt *Link) pclntab() { 120 funcdataBytes := int64(0) 121 ftab := ctxt.Syms.Lookup("runtime.pclntab", 0) 122 ftab.Type = sym.SPCLNTAB 123 ftab.Attr |= sym.AttrReachable 124 125 // See golang.org/s/go12symtab for the format. Briefly: 126 // 8-byte header 127 // nfunc [thearch.ptrsize bytes] 128 // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] 129 // end PC [thearch.ptrsize bytes] 130 // offset to file table [4 bytes] 131 132 // Find container symbols and mark them as such. 133 for _, s := range ctxt.Textp { 134 if s.Outer != nil { 135 s.Outer.Attr |= sym.AttrContainer 136 } 137 } 138 139 // Gather some basic stats and info. 140 var nfunc int32 141 prevSect := ctxt.Textp[0].Sect 142 for _, s := range ctxt.Textp { 143 if !emitPcln(ctxt, s) { 144 continue 145 } 146 nfunc++ 147 if pclntabFirstFunc == nil { 148 pclntabFirstFunc = s 149 } 150 if s.Sect != prevSect { 151 // With multiple text sections, the external linker may insert functions 152 // between the sections, which are not known by Go. This leaves holes in 153 // the PC range covered by the func table. We need to generate an entry 154 // to mark the hole. 155 nfunc++ 156 prevSect = s.Sect 157 } 158 } 159 160 pclntabNfunc = nfunc 161 ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) 162 ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb) 163 ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) 164 ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) 165 ftab.SetUint(ctxt.Arch, 8, uint64(nfunc)) 166 pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize) 167 168 funcnameoff := make(map[string]int32) 169 nameToOffset := func(name string) int32 { 170 nameoff, ok := funcnameoff[name] 171 if !ok { 172 nameoff = ftabaddstring(ftab, name) 173 funcnameoff[name] = nameoff 174 } 175 return nameoff 176 } 177 178 pctaboff := make(map[string]uint32) 179 writepctab := func(off int32, p []byte) int32 { 180 start, ok := pctaboff[string(p)] 181 if !ok { 182 if len(p) > 0 { 183 start = uint32(len(ftab.P)) 184 ftab.AddBytes(p) 185 } 186 pctaboff[string(p)] = start 187 } 188 newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start)) 189 return newoff 190 } 191 192 nfunc = 0 // repurpose nfunc as a running index 193 prevFunc := ctxt.Textp[0] 194 for _, s := range ctxt.Textp { 195 if !emitPcln(ctxt, s) { 196 continue 197 } 198 199 if s.Sect != prevFunc.Sect { 200 // With multiple text sections, there may be a hole here in the address 201 // space (see the comment above). We use an invalid funcoff value to 202 // mark the hole. 203 // See also runtime/symtab.go:findfunc 204 ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size) 205 ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0)) 206 nfunc++ 207 } 208 prevFunc = s 209 210 pcln := s.FuncInfo 211 if pcln == nil { 212 pcln = &pclntabZpcln 213 } 214 215 if len(pcln.InlTree) > 0 { 216 if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex { 217 // Create inlining pcdata table. 218 pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) 219 copy(pcdata, pcln.Pcdata) 220 pcln.Pcdata = pcdata 221 } 222 223 if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree { 224 // Create inline tree funcdata. 225 funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1) 226 funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) 227 copy(funcdata, pcln.Funcdata) 228 copy(funcdataoff, pcln.Funcdataoff) 229 pcln.Funcdata = funcdata 230 pcln.Funcdataoff = funcdataoff 231 } 232 } 233 234 funcstart := int32(len(ftab.P)) 235 funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize 236 237 ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s) 238 ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart)) 239 240 // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func 241 // and package debug/gosym. 242 243 // fixed size of struct, checked below 244 off := funcstart 245 246 end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize) 247 if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { 248 end += 4 249 } 250 ftab.Grow(int64(end)) 251 252 // entry uintptr 253 off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s)) 254 255 // name int32 256 nameoff := nameToOffset(s.Name) 257 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) 258 259 // args int32 260 // TODO: Move into funcinfo. 261 args := uint32(0) 262 if s.FuncInfo != nil { 263 args = uint32(s.FuncInfo.Args) 264 } 265 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) 266 267 // deferreturn 268 deferreturn := uint32(0) 269 lastWasmAddr := uint32(0) 270 for _, r := range s.R { 271 if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR { 272 // Wasm does not have a live variable set at the deferreturn 273 // call itself. Instead it has one identified by the 274 // resumption point immediately preceding the deferreturn. 275 // The wasm code has a R_ADDR relocation which is used to 276 // set the resumption point to PC_B. 277 lastWasmAddr = uint32(r.Add) 278 } 279 if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" { 280 if ctxt.Arch.Family == sys.Wasm { 281 deferreturn = lastWasmAddr - 1 282 } else { 283 // Note: the relocation target is in the call instruction, but 284 // is not necessarily the whole instruction (for instance, on 285 // x86 the relocation applies to bytes [1:5] of the 5 byte call 286 // instruction). 287 deferreturn = uint32(r.Off) 288 switch ctxt.Arch.Family { 289 case sys.AMD64, sys.I386: 290 deferreturn-- 291 case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64: 292 // no change 293 case sys.RISCV64: 294 // TODO(jsing): The JALR instruction is marked with 295 // R_CALLRISCV, whereas the actual reloc is currently 296 // one instruction earlier starting with the AUIPC. 297 deferreturn -= 4 298 case sys.S390X: 299 deferreturn -= 2 300 default: 301 panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family)) 302 } 303 } 304 break // only need one 305 } 306 } 307 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn)) 308 309 if pcln != &pclntabZpcln { 310 renumberfiles(ctxt, pcln.File, &pcln.Pcfile) 311 if false { 312 // Sanity check the new numbering 313 it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) 314 for it.Init(pcln.Pcfile.P); !it.Done; it.Next() { 315 if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) { 316 Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms)) 317 errorexit() 318 } 319 } 320 } 321 } 322 323 if len(pcln.InlTree) > 0 { 324 inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0) 325 inlTreeSym.Type = sym.SRODATA 326 inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK 327 328 for i, call := range pcln.InlTree { 329 // Usually, call.File is already numbered since the file 330 // shows up in the Pcfile table. However, two inlined calls 331 // might overlap exactly so that only the innermost file 332 // appears in the Pcfile table. In that case, this assigns 333 // the outer file a number. 334 numberfile(ctxt, call.File) 335 nameoff := nameToOffset(call.Func) 336 337 inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent)) 338 inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, ""))) 339 // byte 3 is unused 340 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value)) 341 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line)) 342 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff)) 343 inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC)) 344 } 345 346 pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym 347 pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline 348 } 349 350 // pcdata 351 off = writepctab(off, pcln.Pcsp.P) 352 off = writepctab(off, pcln.Pcfile.P) 353 off = writepctab(off, pcln.Pcline.P) 354 off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata)))) 355 356 // funcID uint8 357 var file string 358 if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 { 359 file = s.FuncInfo.File[0].Name 360 } 361 funcID := objabi.GetFuncID(s.Name, file) 362 363 off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) 364 365 // unused 366 off += 2 367 368 // nfuncdata must be the final entry. 369 off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata)))) 370 for i := range pcln.Pcdata { 371 off = writepctab(off, pcln.Pcdata[i].P) 372 } 373 374 // funcdata, must be pointer-aligned and we're only int32-aligned. 375 // Missing funcdata will be 0 (nil pointer). 376 if len(pcln.Funcdata) > 0 { 377 if off&int32(ctxt.Arch.PtrSize-1) != 0 { 378 off += 4 379 } 380 for i := range pcln.Funcdata { 381 dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i) 382 if pcln.Funcdata[i] == nil { 383 ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i])) 384 continue 385 } 386 // TODO: Dedup. 387 funcdataBytes += pcln.Funcdata[i].Size 388 ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i]) 389 } 390 off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize) 391 } 392 393 if off != end { 394 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) 395 errorexit() 396 } 397 398 nfunc++ 399 } 400 401 last := ctxt.Textp[len(ctxt.Textp)-1] 402 pclntabLastFunc = last 403 // Final entry of table is just end pc. 404 ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size) 405 406 // Start file table. 407 start := int32(len(ftab.P)) 408 409 start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) 410 pclntabFiletabOffset = start 411 ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) 412 413 ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4) 414 ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1)) 415 for i := len(ctxt.Filesyms) - 1; i >= 0; i-- { 416 s := ctxt.Filesyms[i] 417 ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) 418 } 419 420 ftab.Size = int64(len(ftab.P)) 421 422 if ctxt.Debugvlog != 0 { 423 ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes) 424 } 425 } 426 427 func gorootFinal() string { 428 root := objabi.GOROOT 429 if final := os.Getenv("GOROOT_FINAL"); final != "" { 430 root = final 431 } 432 return root 433 } 434 435 func expandGoroot(s string) string { 436 const n = len("$GOROOT") 437 if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { 438 return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:])) 439 } 440 return s 441 } 442 443 const ( 444 BUCKETSIZE = 256 * MINFUNC 445 SUBBUCKETS = 16 446 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS 447 NOIDX = 0x7fffffff 448 ) 449 450 // findfunctab generates a lookup table to quickly find the containing 451 // function for a pc. See src/runtime/symtab.go:findfunc for details. 452 func (ctxt *Link) findfunctab() { 453 t := ctxt.Syms.Lookup("runtime.findfunctab", 0) 454 t.Type = sym.SRODATA 455 t.Attr |= sym.AttrReachable 456 t.Attr |= sym.AttrLocal 457 458 // find min and max address 459 min := ctxt.Textp[0].Value 460 lastp := ctxt.Textp[len(ctxt.Textp)-1] 461 max := lastp.Value + lastp.Size 462 463 // for each subbucket, compute the minimum of all symbol indexes 464 // that map to that subbucket. 465 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) 466 467 indexes := make([]int32, n) 468 for i := int32(0); i < n; i++ { 469 indexes[i] = NOIDX 470 } 471 idx := int32(0) 472 for i, s := range ctxt.Textp { 473 if !emitPcln(ctxt, s) { 474 continue 475 } 476 p := s.Value 477 var e *sym.Symbol 478 i++ 479 if i < len(ctxt.Textp) { 480 e = ctxt.Textp[i] 481 } 482 for !emitPcln(ctxt, e) && i < len(ctxt.Textp) { 483 e = ctxt.Textp[i] 484 i++ 485 } 486 q := max 487 if e != nil { 488 q = e.Value 489 } 490 491 //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); 492 for ; p < q; p += SUBBUCKETSIZE { 493 i = int((p - min) / SUBBUCKETSIZE) 494 if indexes[i] > idx { 495 indexes[i] = idx 496 } 497 } 498 499 i = int((q - 1 - min) / SUBBUCKETSIZE) 500 if indexes[i] > idx { 501 indexes[i] = idx 502 } 503 idx++ 504 } 505 506 // allocate table 507 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) 508 509 t.Grow(4*int64(nbuckets) + int64(n)) 510 511 // fill in table 512 for i := int32(0); i < nbuckets; i++ { 513 base := indexes[i*SUBBUCKETS] 514 if base == NOIDX { 515 Errorf(nil, "hole in findfunctab") 516 } 517 t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) 518 for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { 519 idx = indexes[i*SUBBUCKETS+j] 520 if idx == NOIDX { 521 Errorf(nil, "hole in findfunctab") 522 } 523 if idx-base >= 256 { 524 Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) 525 } 526 527 t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) 528 } 529 } 530 }