github.com/bir3/gocompiler@v0.3.205/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 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 9 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 10 "github.com/bir3/gocompiler/src/cmd/internal/sys" 11 "github.com/bir3/gocompiler/src/cmd/link/internal/loader" 12 "github.com/bir3/gocompiler/src/cmd/link/internal/sym" 13 "fmt" 14 "github.com/bir3/gocompiler/src/internal/buildcfg" 15 "os" 16 "path/filepath" 17 "strings" 18 ) 19 20 const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go 21 22 // pclntab holds the state needed for pclntab generation. 23 type pclntab struct { 24 // The first and last functions found. 25 firstFunc, lastFunc loader.Sym 26 27 // Running total size of pclntab. 28 size int64 29 30 // runtime.pclntab's symbols 31 carrier loader.Sym 32 pclntab loader.Sym 33 pcheader loader.Sym 34 funcnametab loader.Sym 35 findfunctab loader.Sym 36 cutab loader.Sym 37 filetab loader.Sym 38 pctab loader.Sym 39 40 // The number of functions + number of TEXT sections - 1. This is such an 41 // unexpected value because platforms that have more than one TEXT section 42 // get a dummy function inserted between because the external linker can place 43 // functions in those areas. We mark those areas as not covered by the Go 44 // runtime. 45 // 46 // On most platforms this is the number of reachable functions. 47 nfunc int32 48 49 // The number of filenames in runtime.filetab. 50 nfiles uint32 51 } 52 53 // addGeneratedSym adds a generator symbol to pclntab, returning the new Sym. 54 // It is the caller's responsibility to save the symbol in state. 55 func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym { 56 size = Rnd(size, int64(ctxt.Arch.PtrSize)) 57 state.size += size 58 s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f) 59 ctxt.loader.SetAttrReachable(s, true) 60 ctxt.loader.SetCarrierSym(s, state.carrier) 61 ctxt.loader.SetAttrNotInSymbolTable(s, true) 62 return s 63 } 64 65 // makePclntab makes a pclntab object, and assembles all the compilation units 66 // we'll need to write pclntab. Returns the pclntab structure, a slice of the 67 // CompilationUnits we need, and a slice of the function symbols we need to 68 // generate pclntab. 69 func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) { 70 ldr := ctxt.loader 71 state := new(pclntab) 72 73 // Gather some basic stats and info. 74 seenCUs := make(map[*sym.CompilationUnit]struct{}) 75 compUnits := []*sym.CompilationUnit{} 76 funcs := []loader.Sym{} 77 78 for _, s := range ctxt.Textp { 79 if !emitPcln(ctxt, s, container) { 80 continue 81 } 82 funcs = append(funcs, s) 83 state.nfunc++ 84 if state.firstFunc == 0 { 85 state.firstFunc = s 86 } 87 state.lastFunc = s 88 89 // We need to keep track of all compilation units we see. Some symbols 90 // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit. 91 cu := ldr.SymUnit(s) 92 if _, ok := seenCUs[cu]; cu != nil && !ok { 93 seenCUs[cu] = struct{}{} 94 cu.PclnIndex = len(compUnits) 95 compUnits = append(compUnits, cu) 96 } 97 } 98 return state, compUnits, funcs 99 } 100 101 func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { 102 // We want to generate func table entries only for the "lowest 103 // level" symbols, not containers of subsymbols. 104 return !container.Has(s) 105 } 106 107 func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 { 108 ldr := ctxt.loader 109 target := ctxt.Target 110 deferreturn := uint32(0) 111 lastWasmAddr := uint32(0) 112 113 relocs := ldr.Relocs(s) 114 for ri := 0; ri < relocs.Count(); ri++ { 115 r := relocs.At(ri) 116 if target.IsWasm() && r.Type() == objabi.R_ADDR { 117 // wasm/ssa.go generates an ARESUMEPOINT just 118 // before the deferreturn call. The "PC" of 119 // the deferreturn call is stored in the 120 // R_ADDR relocation on the ARESUMEPOINT. 121 lastWasmAddr = uint32(r.Add()) 122 } 123 if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) { 124 if target.IsWasm() { 125 deferreturn = lastWasmAddr - 1 126 } else { 127 // Note: the relocation target is in the call instruction, but 128 // is not necessarily the whole instruction (for instance, on 129 // x86 the relocation applies to bytes [1:5] of the 5 byte call 130 // instruction). 131 deferreturn = uint32(r.Off()) 132 switch target.Arch.Family { 133 case sys.AMD64, sys.I386: 134 deferreturn-- 135 case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64: 136 // no change 137 case sys.S390X: 138 deferreturn -= 2 139 default: 140 panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family)) 141 } 142 } 143 break // only need one 144 } 145 } 146 return deferreturn 147 } 148 149 // genInlTreeSym generates the InlTree sym for a function with the 150 // specified FuncInfo. 151 func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym { 152 ldr := ctxt.loader 153 its := ldr.CreateExtSym("", 0) 154 inlTreeSym := ldr.MakeSymbolUpdater(its) 155 // Note: the generated symbol is given a type of sym.SGOFUNC, as a 156 // signal to the symtab() phase that it needs to be grouped in with 157 // other similar symbols (gcdata, etc); the dodata() phase will 158 // eventually switch the type back to SRODATA. 159 inlTreeSym.SetType(sym.SGOFUNC) 160 ldr.SetAttrReachable(its, true) 161 ldr.SetSymAlign(its, 4) // it has 32-bit fields 162 ninl := fi.NumInlTree() 163 for i := 0; i < int(ninl); i++ { 164 call := fi.InlTree(i) 165 nameOff, ok := nameOffsets[call.Func] 166 if !ok { 167 panic("couldn't find function name offset") 168 } 169 170 inlFunc := ldr.FuncInfo(call.Func) 171 var funcID objabi.FuncID 172 startLine := int32(0) 173 if inlFunc.Valid() { 174 funcID = inlFunc.FuncID() 175 startLine = inlFunc.StartLine() 176 } else if !ctxt.linkShared { 177 // Inlined functions are always Go functions, and thus 178 // must have FuncInfo. 179 // 180 // Unfortunately, with -linkshared, the inlined 181 // function may be external symbols (from another 182 // shared library), and we don't load FuncInfo from the 183 // shared library. We will report potentially incorrect 184 // FuncID in this case. See https://go.dev/issue/55954. 185 panic(fmt.Sprintf("inlined function %s missing func info", ldr.SymName(call.Func))) 186 } 187 188 // Construct runtime.inlinedCall value. 189 const size = 16 190 inlTreeSym.SetUint8(arch, int64(i*size+0), uint8(funcID)) 191 // Bytes 1-3 are unused. 192 inlTreeSym.SetUint32(arch, int64(i*size+4), uint32(nameOff)) 193 inlTreeSym.SetUint32(arch, int64(i*size+8), uint32(call.ParentPC)) 194 inlTreeSym.SetUint32(arch, int64(i*size+12), uint32(startLine)) 195 } 196 return its 197 } 198 199 // makeInlSyms returns a map of loader.Sym that are created inlSyms. 200 func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym { 201 ldr := ctxt.loader 202 // Create the inline symbols we need. 203 inlSyms := make(map[loader.Sym]loader.Sym) 204 for _, s := range funcs { 205 if fi := ldr.FuncInfo(s); fi.Valid() { 206 fi.Preload() 207 if fi.NumInlTree() > 0 { 208 inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets) 209 } 210 } 211 } 212 return inlSyms 213 } 214 215 // generatePCHeader creates the runtime.pcheader symbol, setting it up as a 216 // generator to fill in its data later. 217 func (state *pclntab) generatePCHeader(ctxt *Link) { 218 ldr := ctxt.loader 219 textStartOff := int64(8 + 2*ctxt.Arch.PtrSize) 220 size := int64(8 + 8*ctxt.Arch.PtrSize) 221 writeHeader := func(ctxt *Link, s loader.Sym) { 222 header := ctxt.loader.MakeSymbolUpdater(s) 223 224 writeSymOffset := func(off int64, ws loader.Sym) int64 { 225 diff := ldr.SymValue(ws) - ldr.SymValue(s) 226 if diff <= 0 { 227 name := ldr.SymName(ws) 228 panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws))) 229 } 230 return header.SetUintptr(ctxt.Arch, off, uintptr(diff)) 231 } 232 233 // Write header. 234 // Keep in sync with runtime/symtab.go:pcHeader and package debug/gosym. 235 header.SetUint32(ctxt.Arch, 0, 0xfffffff1) 236 header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) 237 header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) 238 off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc)) 239 off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles)) 240 if off != textStartOff { 241 panic(fmt.Sprintf("pcHeader textStartOff: %d != %d", off, textStartOff)) 242 } 243 off += int64(ctxt.Arch.PtrSize) // skip runtimeText relocation 244 off = writeSymOffset(off, state.funcnametab) 245 off = writeSymOffset(off, state.cutab) 246 off = writeSymOffset(off, state.filetab) 247 off = writeSymOffset(off, state.pctab) 248 off = writeSymOffset(off, state.pclntab) 249 if off != size { 250 panic(fmt.Sprintf("pcHeader size: %d != %d", off, size)) 251 } 252 } 253 254 state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) 255 // Create the runtimeText relocation. 256 sb := ldr.MakeSymbolUpdater(state.pcheader) 257 sb.SetAddr(ctxt.Arch, textStartOff, ldr.Lookup("runtime.text", 0)) 258 } 259 260 // walkFuncs iterates over the funcs, calling a function for each unique 261 // function and inlined function. 262 func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) { 263 ldr := ctxt.loader 264 seen := make(map[loader.Sym]struct{}) 265 for _, s := range funcs { 266 if _, ok := seen[s]; !ok { 267 f(s) 268 seen[s] = struct{}{} 269 } 270 271 fi := ldr.FuncInfo(s) 272 if !fi.Valid() { 273 continue 274 } 275 fi.Preload() 276 for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ { 277 call := fi.InlTree(i).Func 278 if _, ok := seen[call]; !ok { 279 f(call) 280 seen[call] = struct{}{} 281 } 282 } 283 } 284 } 285 286 // generateFuncnametab creates the function name table. Returns a map of 287 // func symbol to the name offset in runtime.funcnamtab. 288 func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 { 289 nameOffsets := make(map[loader.Sym]uint32, state.nfunc) 290 291 // The name used by the runtime is the concatenation of the 3 returned strings. 292 // For regular functions, only one returned string is nonempty. 293 // For generic functions, we use three parts so that we can print everything 294 // within the outermost "[]" as "...". 295 nameParts := func(name string) (string, string, string) { 296 i := strings.IndexByte(name, '[') 297 if i < 0 { 298 return name, "", "" 299 } 300 // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5. 301 j := len(name) - 1 302 for j > i && name[j] != ']' { 303 j-- 304 } 305 if j <= i { 306 return name, "", "" 307 } 308 return name[:i], "[...]", name[j+1:] 309 } 310 311 // Write the null terminated strings. 312 writeFuncNameTab := func(ctxt *Link, s loader.Sym) { 313 symtab := ctxt.loader.MakeSymbolUpdater(s) 314 for s, off := range nameOffsets { 315 a, b, c := nameParts(ctxt.loader.SymName(s)) 316 o := int64(off) 317 o = symtab.AddStringAt(o, a) 318 o = symtab.AddStringAt(o, b) 319 _ = symtab.AddCStringAt(o, c) 320 } 321 } 322 323 // Loop through the CUs, and calculate the size needed. 324 var size int64 325 walkFuncs(ctxt, funcs, func(s loader.Sym) { 326 nameOffsets[s] = uint32(size) 327 a, b, c := nameParts(ctxt.loader.SymName(s)) 328 size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate 329 }) 330 331 state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab) 332 return nameOffsets 333 } 334 335 // walkFilenames walks funcs, calling a function for each filename used in each 336 // function's line table. 337 func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) { 338 ldr := ctxt.loader 339 340 // Loop through all functions, finding the filenames we need. 341 for _, s := range funcs { 342 fi := ldr.FuncInfo(s) 343 if !fi.Valid() { 344 continue 345 } 346 fi.Preload() 347 348 cu := ldr.SymUnit(s) 349 for i, nf := 0, int(fi.NumFile()); i < nf; i++ { 350 f(cu, fi.File(i)) 351 } 352 for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ { 353 call := fi.InlTree(i) 354 f(cu, call.File) 355 } 356 } 357 } 358 359 // generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice 360 // of the index at which each CU begins in runtime.cutab. 361 // 362 // Function objects keep track of the files they reference to print the stack. 363 // This function creates a per-CU list of filenames if CU[M] references 364 // files[1-N], the following is generated: 365 // 366 // runtime.cutab: 367 // CU[M] 368 // offsetToFilename[0] 369 // offsetToFilename[1] 370 // .. 371 // 372 // runtime.filetab 373 // filename[0] 374 // filename[1] 375 // 376 // Looking up a filename then becomes: 377 // 0. Given a func, and filename index [K] 378 // 1. Get Func.CUIndex: M := func.cuOffset 379 // 2. Find filename offset: fileOffset := runtime.cutab[M+K] 380 // 3. Get the filename: getcstring(runtime.filetab[fileOffset]) 381 func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 { 382 // On a per-CU basis, keep track of all the filenames we need. 383 // 384 // Note, that we store the filenames in a separate section in the object 385 // files, and deduplicate based on the actual value. It would be better to 386 // store the filenames as symbols, using content addressable symbols (and 387 // then not loading extra filenames), and just use the hash value of the 388 // symbol name to do this cataloging. 389 // 390 // TODO: Store filenames as symbols. (Note this would be easiest if you 391 // also move strings to ALWAYS using the larger content addressable hash 392 // function, and use that hash value for uniqueness testing.) 393 cuEntries := make([]goobj.CUFileIndex, len(compUnits)) 394 fileOffsets := make(map[string]uint32) 395 396 // Walk the filenames. 397 // We store the total filename string length we need to load, and the max 398 // file index we've seen per CU so we can calculate how large the 399 // CU->global table needs to be. 400 var fileSize int64 401 walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) { 402 // Note we use the raw filename for lookup, but use the expanded filename 403 // when we save the size. 404 filename := cu.FileTable[i] 405 if _, ok := fileOffsets[filename]; !ok { 406 fileOffsets[filename] = uint32(fileSize) 407 fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate 408 } 409 410 // Find the maximum file index we've seen. 411 if cuEntries[cu.PclnIndex] < i+1 { 412 cuEntries[cu.PclnIndex] = i + 1 // Store max + 1 413 } 414 }) 415 416 // Calculate the size of the runtime.cutab variable. 417 var totalEntries uint32 418 cuOffsets := make([]uint32, len(cuEntries)) 419 for i, entries := range cuEntries { 420 // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the 421 // running total of all cu indices we've needed to store so far, not the 422 // number of bytes we've stored so far. 423 cuOffsets[i] = totalEntries 424 totalEntries += uint32(entries) 425 } 426 427 // Write cutab. 428 writeCutab := func(ctxt *Link, s loader.Sym) { 429 sb := ctxt.loader.MakeSymbolUpdater(s) 430 431 var off int64 432 for i, max := range cuEntries { 433 // Write the per CU LUT. 434 cu := compUnits[i] 435 for j := goobj.CUFileIndex(0); j < max; j++ { 436 fileOffset, ok := fileOffsets[cu.FileTable[j]] 437 if !ok { 438 // We're looping through all possible file indices. It's possible a file's 439 // been deadcode eliminated, and although it's a valid file in the CU, it's 440 // not needed in this binary. When that happens, use an invalid offset. 441 fileOffset = ^uint32(0) 442 } 443 off = sb.SetUint32(ctxt.Arch, off, fileOffset) 444 } 445 } 446 } 447 state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab) 448 449 // Write filetab. 450 writeFiletab := func(ctxt *Link, s loader.Sym) { 451 sb := ctxt.loader.MakeSymbolUpdater(s) 452 453 // Write the strings. 454 for filename, loc := range fileOffsets { 455 sb.AddStringAt(int64(loc), expandFile(filename)) 456 } 457 } 458 state.nfiles = uint32(len(fileOffsets)) 459 state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab) 460 461 return cuOffsets 462 } 463 464 // generatePctab creates the runtime.pctab variable, holding all the 465 // deduplicated pcdata. 466 func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) { 467 ldr := ctxt.loader 468 469 // Pctab offsets of 0 are considered invalid in the runtime. We respect 470 // that by just padding a single byte at the beginning of runtime.pctab, 471 // that way no real offsets can be zero. 472 size := int64(1) 473 474 // Walk the functions, finding offset to store each pcdata. 475 seen := make(map[loader.Sym]struct{}) 476 saveOffset := func(pcSym loader.Sym) { 477 if _, ok := seen[pcSym]; !ok { 478 datSize := ldr.SymSize(pcSym) 479 if datSize != 0 { 480 ldr.SetSymValue(pcSym, size) 481 } else { 482 // Invalid PC data, record as zero. 483 ldr.SetSymValue(pcSym, 0) 484 } 485 size += datSize 486 seen[pcSym] = struct{}{} 487 } 488 } 489 var pcsp, pcline, pcfile, pcinline loader.Sym 490 var pcdata []loader.Sym 491 for _, s := range funcs { 492 fi := ldr.FuncInfo(s) 493 if !fi.Valid() { 494 continue 495 } 496 fi.Preload() 497 pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) 498 499 pcSyms := []loader.Sym{pcsp, pcfile, pcline} 500 for _, pcSym := range pcSyms { 501 saveOffset(pcSym) 502 } 503 for _, pcSym := range pcdata { 504 saveOffset(pcSym) 505 } 506 if fi.NumInlTree() > 0 { 507 saveOffset(pcinline) 508 } 509 } 510 511 // TODO: There is no reason we need a generator for this variable, and it 512 // could be moved to a carrier symbol. However, carrier symbols containing 513 // carrier symbols don't work yet (as of Aug 2020). Once this is fixed, 514 // runtime.pctab could just be a carrier sym. 515 writePctab := func(ctxt *Link, s loader.Sym) { 516 ldr := ctxt.loader 517 sb := ldr.MakeSymbolUpdater(s) 518 for sym := range seen { 519 sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym)) 520 } 521 } 522 523 state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab) 524 } 525 526 // numPCData returns the number of PCData syms for the FuncInfo. 527 // NB: Preload must be called on valid FuncInfos before calling this function. 528 func numPCData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 { 529 if !fi.Valid() { 530 return 0 531 } 532 numPCData := uint32(ldr.NumPcdata(s)) 533 if fi.NumInlTree() > 0 { 534 if numPCData < objabi.PCDATA_InlTreeIndex+1 { 535 numPCData = objabi.PCDATA_InlTreeIndex + 1 536 } 537 } 538 return numPCData 539 } 540 541 // generateFunctab creates the runtime.functab 542 // 543 // runtime.functab contains two things: 544 // 545 // - pc->func look up table. 546 // - array of func objects, interleaved with pcdata and funcdata 547 func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { 548 // Calculate the size of the table. 549 size, startLocations := state.calculateFunctabSize(ctxt, funcs) 550 writePcln := func(ctxt *Link, s loader.Sym) { 551 ldr := ctxt.loader 552 sb := ldr.MakeSymbolUpdater(s) 553 // Write the data. 554 writePCToFunc(ctxt, sb, funcs, startLocations) 555 writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets) 556 } 557 state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln) 558 } 559 560 // funcData returns the funcdata and offsets for the FuncInfo. 561 // The funcdata are written into runtime.functab after each func 562 // object. This is a helper function to make querying the FuncInfo object 563 // cleaner. 564 // 565 // NB: Preload must be called on the FuncInfo before calling. 566 // NB: fdSyms is used as scratch space. 567 func funcData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym) []loader.Sym { 568 fdSyms = fdSyms[:0] 569 if fi.Valid() { 570 fdSyms = ldr.Funcdata(s, fdSyms) 571 if fi.NumInlTree() > 0 { 572 if len(fdSyms) < objabi.FUNCDATA_InlTree+1 { 573 fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...) 574 } 575 fdSyms[objabi.FUNCDATA_InlTree] = inlSym 576 } 577 } 578 return fdSyms 579 } 580 581 // calculateFunctabSize calculates the size of the pclntab, and the offsets in 582 // the output buffer for individual func entries. 583 func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) { 584 ldr := ctxt.loader 585 startLocations := make([]uint32, len(funcs)) 586 587 // Allocate space for the pc->func table. This structure consists of a pc offset 588 // and an offset to the func structure. After that, we have a single pc 589 // value that marks the end of the last function in the binary. 590 size := int64(int(state.nfunc)*2*4 + 4) 591 592 // Now find the space for the func objects. We do this in a running manner, 593 // so that we can find individual starting locations. 594 for i, s := range funcs { 595 size = Rnd(size, int64(ctxt.Arch.PtrSize)) 596 startLocations[i] = uint32(size) 597 fi := ldr.FuncInfo(s) 598 size += funcSize 599 if fi.Valid() { 600 fi.Preload() 601 numFuncData := ldr.NumFuncdata(s) 602 if fi.NumInlTree() > 0 { 603 if numFuncData < objabi.FUNCDATA_InlTree+1 { 604 numFuncData = objabi.FUNCDATA_InlTree + 1 605 } 606 } 607 size += int64(numPCData(ldr, s, fi) * 4) 608 size += int64(numFuncData * 4) 609 } 610 } 611 612 return size, startLocations 613 } 614 615 // writePCToFunc writes the PC->func lookup table. 616 func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) { 617 ldr := ctxt.loader 618 textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) 619 pcOff := func(s loader.Sym) uint32 { 620 off := ldr.SymValue(s) - textStart 621 if off < 0 { 622 panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) 623 } 624 return uint32(off) 625 } 626 for i, s := range funcs { 627 sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s)) 628 sb.SetUint32(ctxt.Arch, int64((i*2+1)*4), startLocations[i]) 629 } 630 631 // Final entry of table is just end pc offset. 632 lastFunc := funcs[len(funcs)-1] 633 sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc))) 634 } 635 636 // writeFuncs writes the func structures and pcdata to runtime.functab. 637 func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { 638 ldr := ctxt.loader 639 deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer) 640 gofunc := ldr.Lookup("go:func.*", 0) 641 gofuncBase := ldr.SymValue(gofunc) 642 textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) 643 funcdata := []loader.Sym{} 644 var pcsp, pcfile, pcline, pcinline loader.Sym 645 var pcdata []loader.Sym 646 647 // Write the individual func objects. 648 for i, s := range funcs { 649 startLine := int32(0) 650 fi := ldr.FuncInfo(s) 651 if fi.Valid() { 652 fi.Preload() 653 pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) 654 startLine = fi.StartLine() 655 } 656 657 off := int64(startLocations[i]) 658 // entryOff uint32 (offset of func entry PC from textStart) 659 entryOff := ldr.SymValue(s) - textStart 660 if entryOff < 0 { 661 panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) 662 } 663 off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff)) 664 665 // nameOff int32 666 nameOff, ok := nameOffsets[s] 667 if !ok { 668 panic("couldn't find function name offset") 669 } 670 off = sb.SetUint32(ctxt.Arch, off, uint32(nameOff)) 671 672 // args int32 673 // TODO: Move into funcinfo. 674 args := uint32(0) 675 if fi.Valid() { 676 args = uint32(fi.Args()) 677 } 678 off = sb.SetUint32(ctxt.Arch, off, args) 679 680 // deferreturn 681 deferreturn := computeDeferReturn(ctxt, deferReturnSym, s) 682 off = sb.SetUint32(ctxt.Arch, off, deferreturn) 683 684 // pcdata 685 if fi.Valid() { 686 off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcsp))) 687 off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcfile))) 688 off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcline))) 689 } else { 690 off += 12 691 } 692 off = sb.SetUint32(ctxt.Arch, off, uint32(numPCData(ldr, s, fi))) 693 694 // Store the offset to compilation unit's file table. 695 cuIdx := ^uint32(0) 696 if cu := ldr.SymUnit(s); cu != nil { 697 cuIdx = cuOffsets[cu.PclnIndex] 698 } 699 off = sb.SetUint32(ctxt.Arch, off, cuIdx) 700 701 // startLine int32 702 off = sb.SetUint32(ctxt.Arch, off, uint32(startLine)) 703 704 // funcID uint8 705 var funcID objabi.FuncID 706 if fi.Valid() { 707 funcID = fi.FuncID() 708 } 709 off = sb.SetUint8(ctxt.Arch, off, uint8(funcID)) 710 711 // flag uint8 712 var flag objabi.FuncFlag 713 if fi.Valid() { 714 flag = fi.FuncFlag() 715 } 716 off = sb.SetUint8(ctxt.Arch, off, uint8(flag)) 717 718 off += 1 // pad 719 720 // nfuncdata must be the final entry. 721 funcdata = funcData(ldr, s, fi, 0, funcdata) 722 off = sb.SetUint8(ctxt.Arch, off, uint8(len(funcdata))) 723 724 // Output the pcdata. 725 if fi.Valid() { 726 for j, pcSym := range pcdata { 727 sb.SetUint32(ctxt.Arch, off+int64(j*4), uint32(ldr.SymValue(pcSym))) 728 } 729 if fi.NumInlTree() > 0 { 730 sb.SetUint32(ctxt.Arch, off+objabi.PCDATA_InlTreeIndex*4, uint32(ldr.SymValue(pcinline))) 731 } 732 } 733 734 // Write funcdata refs as offsets from go:func.* and go:funcrel.*. 735 funcdata = funcData(ldr, s, fi, inlSyms[s], funcdata) 736 // Missing funcdata will be ^0. See runtime/symtab.go:funcdata. 737 off = int64(startLocations[i] + funcSize + numPCData(ldr, s, fi)*4) 738 for j := range funcdata { 739 dataoff := off + int64(4*j) 740 fdsym := funcdata[j] 741 if fdsym == 0 { 742 sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value" 743 continue 744 } 745 746 if outer := ldr.OuterSym(fdsym); outer != gofunc { 747 panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer))) 748 } 749 sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase)) 750 } 751 } 752 } 753 754 // pclntab initializes the pclntab symbol with 755 // runtime function and file name information. 756 757 // pclntab generates the pcln table for the link output. 758 func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { 759 // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the 760 // layout and data has changed since that time. 761 // 762 // As of August 2020, here's the layout of pclntab: 763 // 764 // .gopclntab/__gopclntab [elf/macho section] 765 // runtime.pclntab 766 // Carrier symbol for the entire pclntab section. 767 // 768 // runtime.pcheader (see: runtime/symtab.go:pcHeader) 769 // 8-byte magic 770 // nfunc [thearch.ptrsize bytes] 771 // offset to runtime.funcnametab from the beginning of runtime.pcheader 772 // offset to runtime.pclntab_old from beginning of runtime.pcheader 773 // 774 // runtime.funcnametab 775 // []list of null terminated function names 776 // 777 // runtime.cutab 778 // for i=0..#CUs 779 // for j=0..#max used file index in CU[i] 780 // uint32 offset into runtime.filetab for the filename[j] 781 // 782 // runtime.filetab 783 // []null terminated filename strings 784 // 785 // runtime.pctab 786 // []byte of deduplicated pc data. 787 // 788 // runtime.functab 789 // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] 790 // end PC [thearch.ptrsize bytes] 791 // func structures, pcdata offsets, func data. 792 793 state, compUnits, funcs := makePclntab(ctxt, container) 794 795 ldr := ctxt.loader 796 state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) 797 ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) 798 ldr.SetAttrReachable(state.carrier, true) 799 setCarrierSym(sym.SPCLNTAB, state.carrier) 800 801 state.generatePCHeader(ctxt) 802 nameOffsets := state.generateFuncnametab(ctxt, funcs) 803 cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs) 804 state.generatePctab(ctxt, funcs) 805 inlSyms := makeInlSyms(ctxt, funcs, nameOffsets) 806 state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets) 807 808 return state 809 } 810 811 func gorootFinal() string { 812 root := buildcfg.GOROOT 813 if final := os.Getenv("GOROOT_FINAL"); final != "" { 814 root = final 815 } 816 return root 817 } 818 819 func expandGoroot(s string) string { 820 const n = len("$GOROOT") 821 if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { 822 if final := gorootFinal(); final != "" { 823 return filepath.ToSlash(filepath.Join(final, s[n:])) 824 } 825 } 826 return s 827 } 828 829 const ( 830 BUCKETSIZE = 256 * MINFUNC 831 SUBBUCKETS = 16 832 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS 833 NOIDX = 0x7fffffff 834 ) 835 836 // findfunctab generates a lookup table to quickly find the containing 837 // function for a pc. See src/runtime/symtab.go:findfunc for details. 838 func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) { 839 ldr := ctxt.loader 840 841 // find min and max address 842 min := ldr.SymValue(ctxt.Textp[0]) 843 lastp := ctxt.Textp[len(ctxt.Textp)-1] 844 max := ldr.SymValue(lastp) + ldr.SymSize(lastp) 845 846 // for each subbucket, compute the minimum of all symbol indexes 847 // that map to that subbucket. 848 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) 849 850 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) 851 852 size := 4*int64(nbuckets) + int64(n) 853 854 writeFindFuncTab := func(_ *Link, s loader.Sym) { 855 t := ldr.MakeSymbolUpdater(s) 856 857 indexes := make([]int32, n) 858 for i := int32(0); i < n; i++ { 859 indexes[i] = NOIDX 860 } 861 idx := int32(0) 862 for i, s := range ctxt.Textp { 863 if !emitPcln(ctxt, s, container) { 864 continue 865 } 866 p := ldr.SymValue(s) 867 var e loader.Sym 868 i++ 869 if i < len(ctxt.Textp) { 870 e = ctxt.Textp[i] 871 } 872 for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) { 873 e = ctxt.Textp[i] 874 i++ 875 } 876 q := max 877 if e != 0 { 878 q = ldr.SymValue(e) 879 } 880 881 //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); 882 for ; p < q; p += SUBBUCKETSIZE { 883 i = int((p - min) / SUBBUCKETSIZE) 884 if indexes[i] > idx { 885 indexes[i] = idx 886 } 887 } 888 889 i = int((q - 1 - min) / SUBBUCKETSIZE) 890 if indexes[i] > idx { 891 indexes[i] = idx 892 } 893 idx++ 894 } 895 896 // fill in table 897 for i := int32(0); i < nbuckets; i++ { 898 base := indexes[i*SUBBUCKETS] 899 if base == NOIDX { 900 Errorf(nil, "hole in findfunctab") 901 } 902 t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) 903 for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { 904 idx = indexes[i*SUBBUCKETS+j] 905 if idx == NOIDX { 906 Errorf(nil, "hole in findfunctab") 907 } 908 if idx-base >= 256 { 909 Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) 910 } 911 912 t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) 913 } 914 } 915 } 916 917 state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) 918 ldr.SetAttrReachable(state.findfunctab, true) 919 ldr.SetAttrLocal(state.findfunctab, true) 920 } 921 922 // findContainerSyms returns a bitmap, indexed by symbol number, where there's 923 // a 1 for every container symbol. 924 func (ctxt *Link) findContainerSyms() loader.Bitmap { 925 ldr := ctxt.loader 926 container := loader.MakeBitmap(ldr.NSym()) 927 // Find container symbols and mark them as such. 928 for _, s := range ctxt.Textp { 929 outer := ldr.OuterSym(s) 930 if outer != 0 { 931 container.Set(outer) 932 } 933 } 934 return container 935 }