2016-03-01 22:57:46 +00:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
2015-02-27 22:57:28 -05:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package ld
|
|
|
|
|
|
|
|
|
|
import (
|
2020-08-02 19:36:28 -04:00
|
|
|
"cmd/internal/goobj"
|
2017-04-18 12:53:25 -07:00
|
|
|
"cmd/internal/objabi"
|
2018-10-04 16:32:21 -07:00
|
|
|
"cmd/internal/sys"
|
2020-04-07 17:08:00 -04:00
|
|
|
"cmd/link/internal/loader"
|
2017-10-04 17:54:04 -04:00
|
|
|
"cmd/link/internal/sym"
|
cmd/compile, cmd/link, runtime: make defers low-cost through inline code and extra funcdata
Generate inline code at defer time to save the args of defer calls to unique
(autotmp) stack slots, and generate inline code at exit time to check which defer
calls were made and make the associated function/method/interface calls. We
remember that a particular defer statement was reached by storing in the deferBits
variable (always stored on the stack). At exit time, we check the bits of the
deferBits variable to determine which defer function calls to make (in reverse
order). These low-cost defers are only used for functions where no defers
appear in loops. In addition, we don't do these low-cost defers if there are too
many defer statements or too many exits in a function (to limit code increase).
When a function uses open-coded defers, we produce extra
FUNCDATA_OpenCodedDeferInfo information that specifies the number of defers, and
for each defer, the stack slots where the closure and associated args have been
stored. The funcdata also includes the location of the deferBits variable.
Therefore, for panics, we can use this funcdata to determine exactly which defers
are active, and call the appropriate functions/methods/closures with the correct
arguments for each active defer.
In order to unwind the stack correctly after a recover(), we need to add an extra
code segment to functions with open-coded defers that simply calls deferreturn()
and returns. This segment is not reachable by the normal function, but is returned
to by the runtime during recovery. We set the liveness information of this
deferreturn() to be the same as the liveness at the first function call during the
last defer exit code (so all return values and all stack slots needed by the defer
calls will be live).
I needed to increase the stackguard constant from 880 to 896, because of a small
amount of new code in deferreturn().
The -N flag disables open-coded defers. '-d defer' prints out the kind of defer
being used at each defer statement (heap-allocated, stack-allocated, or
open-coded).
Cost of defer statement [ go test -run NONE -bench BenchmarkDefer$ runtime ]
With normal (stack-allocated) defers only: 35.4 ns/op
With open-coded defers: 5.6 ns/op
Cost of function call alone (remove defer keyword): 4.4 ns/op
Text size increase (including funcdata) for go binary without/with open-coded defers: 0.09%
The average size increase (including funcdata) for only the functions that use
open-coded defers is 1.1%.
The cost of a panic followed by a recover got noticeably slower, since panic
processing now requires a scan of the stack for open-coded defer frames. This scan
is required, even if no frames are using open-coded defers:
Cost of panic and recover [ go test -run NONE -bench BenchmarkPanicRecover runtime ]
Without open-coded defers: 62.0 ns/op
With open-coded defers: 255 ns/op
A CGO Go-to-C-to-Go benchmark got noticeably faster because of open-coded defers:
CGO Go-to-C-to-Go benchmark [cd misc/cgo/test; go test -run NONE -bench BenchmarkCGoCallback ]
Without open-coded defers: 443 ns/op
With open-coded defers: 347 ns/op
Updates #14939 (defer performance)
Updates #34481 (design doc)
Change-Id: I63b1a60d1ebf28126f55ee9fd7ecffe9cb23d1ff
Reviewed-on: https://go-review.googlesource.com/c/go/+/202340
Reviewed-by: Austin Clements <austin@google.com>
2019-06-24 12:59:22 -07:00
|
|
|
"fmt"
|
2015-12-29 10:16:40 -05:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2015-02-27 22:57:28 -05:00
|
|
|
)
|
|
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
// pclntab holds the state needed for pclntab generation.
|
|
|
|
|
type pclntab struct {
|
2020-08-18 13:38:04 -04:00
|
|
|
// The size of the func object in the runtime.
|
|
|
|
|
funcSize uint32
|
|
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
// The first and last functions found.
|
|
|
|
|
firstFunc, lastFunc loader.Sym
|
|
|
|
|
|
2020-07-16 16:18:49 -04:00
|
|
|
// Running total size of pclntab.
|
|
|
|
|
size int64
|
|
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
// runtime.pclntab's symbols
|
|
|
|
|
carrier loader.Sym
|
|
|
|
|
pclntab loader.Sym
|
|
|
|
|
pcheader loader.Sym
|
2020-07-16 16:18:49 -04:00
|
|
|
funcnametab loader.Sym
|
2020-07-14 13:12:58 -04:00
|
|
|
findfunctab loader.Sym
|
2020-08-03 13:19:46 -04:00
|
|
|
cutab loader.Sym
|
|
|
|
|
filetab loader.Sym
|
2020-08-12 19:26:53 -04:00
|
|
|
pctab loader.Sym
|
2020-07-14 13:12:58 -04:00
|
|
|
|
|
|
|
|
// The number of functions + number of TEXT sections - 1. This is such an
|
|
|
|
|
// unexpected value because platforms that have more than one TEXT section
|
|
|
|
|
// get a dummy function inserted between because the external linker can place
|
|
|
|
|
// functions in those areas. We mark those areas as not covered by the Go
|
|
|
|
|
// runtime.
|
|
|
|
|
//
|
|
|
|
|
// On most platforms this is the number of reachable functions.
|
|
|
|
|
nfunc int32
|
2020-07-16 16:18:49 -04:00
|
|
|
|
2020-08-03 13:19:46 -04:00
|
|
|
// The number of filenames in runtime.filetab.
|
|
|
|
|
nfiles uint32
|
2020-07-16 16:18:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
|
2021-02-17 01:48:21 +00:00
|
|
|
// It is the caller's responsibility to save they symbol in state.
|
2020-07-16 16:18:49 -04:00
|
|
|
func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym {
|
|
|
|
|
size = Rnd(size, int64(ctxt.Arch.PtrSize))
|
|
|
|
|
state.size += size
|
|
|
|
|
s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f)
|
|
|
|
|
ctxt.loader.SetAttrReachable(s, true)
|
|
|
|
|
ctxt.loader.SetCarrierSym(s, state.carrier)
|
2020-08-03 17:59:41 -04:00
|
|
|
ctxt.loader.SetAttrNotInSymbolTable(s, true)
|
2020-07-16 16:18:49 -04:00
|
|
|
return s
|
2020-07-14 13:12:58 -04:00
|
|
|
}
|
|
|
|
|
|
2020-07-23 15:15:26 -04:00
|
|
|
// makePclntab makes a pclntab object, and assembles all the compilation units
|
2020-08-13 12:21:18 -04:00
|
|
|
// we'll need to write pclntab. Returns the pclntab structure, a slice of the
|
|
|
|
|
// CompilationUnits we need, and a slice of the function symbols we need to
|
|
|
|
|
// generate pclntab.
|
|
|
|
|
func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
|
2020-07-14 13:12:58 -04:00
|
|
|
ldr := ctxt.loader
|
|
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
state := &pclntab{
|
|
|
|
|
// This is the size of the _func object in runtime/runtime2.go.
|
|
|
|
|
funcSize: uint32(ctxt.Arch.PtrSize + 9*4),
|
|
|
|
|
}
|
2020-07-14 13:12:58 -04:00
|
|
|
|
|
|
|
|
// Gather some basic stats and info.
|
2020-07-23 15:15:26 -04:00
|
|
|
seenCUs := make(map[*sym.CompilationUnit]struct{})
|
2020-07-14 13:12:58 -04:00
|
|
|
prevSect := ldr.SymSect(ctxt.Textp[0])
|
2020-07-23 15:15:26 -04:00
|
|
|
compUnits := []*sym.CompilationUnit{}
|
2020-08-13 12:21:18 -04:00
|
|
|
funcs := []loader.Sym{}
|
2020-07-23 15:15:26 -04:00
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
for _, s := range ctxt.Textp {
|
|
|
|
|
if !emitPcln(ctxt, s, container) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2020-08-13 12:21:18 -04:00
|
|
|
funcs = append(funcs, s)
|
2020-07-14 13:12:58 -04:00
|
|
|
state.nfunc++
|
|
|
|
|
if state.firstFunc == 0 {
|
|
|
|
|
state.firstFunc = s
|
|
|
|
|
}
|
|
|
|
|
state.lastFunc = s
|
|
|
|
|
ss := ldr.SymSect(s)
|
|
|
|
|
if ss != prevSect {
|
|
|
|
|
// With multiple text sections, the external linker may
|
|
|
|
|
// insert functions between the sections, which are not
|
|
|
|
|
// known by Go. This leaves holes in the PC range covered
|
|
|
|
|
// by the func table. We need to generate an entry to mark
|
|
|
|
|
// the hole.
|
|
|
|
|
state.nfunc++
|
|
|
|
|
prevSect = ss
|
|
|
|
|
}
|
2020-07-23 15:15:26 -04:00
|
|
|
|
|
|
|
|
// We need to keep track of all compilation units we see. Some symbols
|
|
|
|
|
// (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit.
|
|
|
|
|
cu := ldr.SymUnit(s)
|
|
|
|
|
if _, ok := seenCUs[cu]; cu != nil && !ok {
|
|
|
|
|
seenCUs[cu] = struct{}{}
|
|
|
|
|
cu.PclnIndex = len(compUnits)
|
|
|
|
|
compUnits = append(compUnits, cu)
|
|
|
|
|
}
|
2020-07-14 13:12:58 -04:00
|
|
|
}
|
2020-08-13 12:21:18 -04:00
|
|
|
return state, compUnits, funcs
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2020-04-10 10:30:27 -04:00
|
|
|
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
|
2020-04-07 17:08:00 -04:00
|
|
|
// We want to generate func table entries only for the "lowest
|
|
|
|
|
// level" symbols, not containers of subsymbols.
|
2020-04-10 10:30:27 -04:00
|
|
|
return !container.Has(s)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 12:21:18 -04:00
|
|
|
func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
target := ctxt.Target
|
2020-04-07 17:08:00 -04:00
|
|
|
deferreturn := uint32(0)
|
|
|
|
|
lastWasmAddr := uint32(0)
|
|
|
|
|
|
2020-08-13 12:21:18 -04:00
|
|
|
relocs := ldr.Relocs(s)
|
2020-04-07 17:08:00 -04:00
|
|
|
for ri := 0; ri < relocs.Count(); ri++ {
|
2020-07-29 13:26:50 -04:00
|
|
|
r := relocs.At(ri)
|
2020-04-07 17:08:00 -04:00
|
|
|
if target.IsWasm() && r.Type() == objabi.R_ADDR {
|
|
|
|
|
// Wasm does not have a live variable set at the deferreturn
|
|
|
|
|
// call itself. Instead it has one identified by the
|
|
|
|
|
// resumption point immediately preceding the deferreturn.
|
|
|
|
|
// The wasm code has a R_ADDR relocation which is used to
|
|
|
|
|
// set the resumption point to PC_B.
|
|
|
|
|
lastWasmAddr = uint32(r.Add())
|
|
|
|
|
}
|
2020-08-13 12:21:18 -04:00
|
|
|
if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
|
2020-04-07 17:08:00 -04:00
|
|
|
if target.IsWasm() {
|
|
|
|
|
deferreturn = lastWasmAddr - 1
|
|
|
|
|
} else {
|
|
|
|
|
// Note: the relocation target is in the call instruction, but
|
|
|
|
|
// is not necessarily the whole instruction (for instance, on
|
|
|
|
|
// x86 the relocation applies to bytes [1:5] of the 5 byte call
|
|
|
|
|
// instruction).
|
|
|
|
|
deferreturn = uint32(r.Off())
|
|
|
|
|
switch target.Arch.Family {
|
|
|
|
|
case sys.AMD64, sys.I386:
|
|
|
|
|
deferreturn--
|
|
|
|
|
case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
|
|
|
|
|
// no change
|
|
|
|
|
case sys.RISCV64:
|
|
|
|
|
// TODO(jsing): The JALR instruction is marked with
|
|
|
|
|
// R_CALLRISCV, whereas the actual reloc is currently
|
|
|
|
|
// one instruction earlier starting with the AUIPC.
|
|
|
|
|
deferreturn -= 4
|
|
|
|
|
case sys.S390X:
|
|
|
|
|
deferreturn -= 2
|
|
|
|
|
default:
|
|
|
|
|
panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break // only need one
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return deferreturn
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-10 13:07:08 -04:00
|
|
|
// genInlTreeSym generates the InlTree sym for a function with the
|
|
|
|
|
// specified FuncInfo.
|
2020-08-13 12:21:18 -04:00
|
|
|
func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
|
|
|
|
|
ldr := ctxt.loader
|
2020-04-10 13:07:08 -04:00
|
|
|
its := ldr.CreateExtSym("", 0)
|
2020-04-07 17:08:00 -04:00
|
|
|
inlTreeSym := ldr.MakeSymbolUpdater(its)
|
2020-04-10 13:07:08 -04:00
|
|
|
// Note: the generated symbol is given a type of sym.SGOFUNC, as a
|
|
|
|
|
// signal to the symtab() phase that it needs to be grouped in with
|
|
|
|
|
// other similar symbols (gcdata, etc); the dodata() phase will
|
|
|
|
|
// eventually switch the type back to SRODATA.
|
|
|
|
|
inlTreeSym.SetType(sym.SGOFUNC)
|
2020-04-07 17:08:00 -04:00
|
|
|
ldr.SetAttrReachable(its, true)
|
|
|
|
|
ninl := fi.NumInlTree()
|
|
|
|
|
for i := 0; i < int(ninl); i++ {
|
|
|
|
|
call := fi.InlTree(i)
|
2020-08-03 13:19:46 -04:00
|
|
|
val := call.File
|
2020-08-13 12:21:18 -04:00
|
|
|
nameoff, ok := nameOffsets[call.Func]
|
2020-07-16 16:18:49 -04:00
|
|
|
if !ok {
|
|
|
|
|
panic("couldn't find function name offset")
|
|
|
|
|
}
|
2020-04-07 17:08:00 -04:00
|
|
|
|
|
|
|
|
inlTreeSym.SetUint16(arch, int64(i*20+0), uint16(call.Parent))
|
2020-07-21 15:53:30 -04:00
|
|
|
inlFunc := ldr.FuncInfo(call.Func)
|
|
|
|
|
|
|
|
|
|
var funcID objabi.FuncID
|
|
|
|
|
if inlFunc.Valid() {
|
|
|
|
|
funcID = inlFunc.FuncID()
|
|
|
|
|
}
|
|
|
|
|
inlTreeSym.SetUint8(arch, int64(i*20+2), uint8(funcID))
|
|
|
|
|
|
2020-04-07 17:08:00 -04:00
|
|
|
// byte 3 is unused
|
|
|
|
|
inlTreeSym.SetUint32(arch, int64(i*20+4), uint32(val))
|
|
|
|
|
inlTreeSym.SetUint32(arch, int64(i*20+8), uint32(call.Line))
|
|
|
|
|
inlTreeSym.SetUint32(arch, int64(i*20+12), uint32(nameoff))
|
|
|
|
|
inlTreeSym.SetUint32(arch, int64(i*20+16), uint32(call.ParentPC))
|
|
|
|
|
}
|
|
|
|
|
return its
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// makeInlSyms returns a map of loader.Sym that are created inlSyms.
|
|
|
|
|
func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
// Create the inline symbols we need.
|
|
|
|
|
inlSyms := make(map[loader.Sym]loader.Sym)
|
|
|
|
|
for _, s := range funcs {
|
|
|
|
|
if fi := ldr.FuncInfo(s); fi.Valid() {
|
|
|
|
|
fi.Preload()
|
|
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return inlSyms
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 14:30:16 -04:00
|
|
|
// generatePCHeader creates the runtime.pcheader symbol, setting it up as a
|
|
|
|
|
// generator to fill in its data later.
|
2020-07-14 13:12:58 -04:00
|
|
|
func (state *pclntab) generatePCHeader(ctxt *Link) {
|
2020-06-24 14:30:16 -04:00
|
|
|
writeHeader := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
header := ctxt.loader.MakeSymbolUpdater(s)
|
|
|
|
|
|
2020-07-16 16:18:49 -04:00
|
|
|
writeSymOffset := func(off int64, ws loader.Sym) int64 {
|
|
|
|
|
diff := ldr.SymValue(ws) - ldr.SymValue(s)
|
|
|
|
|
if diff <= 0 {
|
|
|
|
|
name := ldr.SymName(ws)
|
|
|
|
|
panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws)))
|
|
|
|
|
}
|
|
|
|
|
return header.SetUintptr(ctxt.Arch, off, uintptr(diff))
|
2020-06-24 14:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write header.
|
|
|
|
|
// Keep in sync with runtime/symtab.go:pcHeader.
|
|
|
|
|
header.SetUint32(ctxt.Arch, 0, 0xfffffffa)
|
|
|
|
|
header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
|
|
|
|
|
header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
|
2020-07-14 13:12:58 -04:00
|
|
|
off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
|
2020-08-03 13:19:46 -04:00
|
|
|
off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
|
2020-07-16 16:18:49 -04:00
|
|
|
off = writeSymOffset(off, state.funcnametab)
|
2020-08-03 13:19:46 -04:00
|
|
|
off = writeSymOffset(off, state.cutab)
|
|
|
|
|
off = writeSymOffset(off, state.filetab)
|
2020-08-12 19:26:53 -04:00
|
|
|
off = writeSymOffset(off, state.pctab)
|
2020-07-16 16:18:49 -04:00
|
|
|
off = writeSymOffset(off, state.pclntab)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-12 19:26:53 -04:00
|
|
|
size := int64(8 + 7*ctxt.Arch.PtrSize)
|
2020-07-16 16:18:49 -04:00
|
|
|
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 12:21:18 -04:00
|
|
|
// walkFuncs iterates over the funcs, calling a function for each unique
|
2020-07-16 16:18:49 -04:00
|
|
|
// function and inlined function.
|
2020-08-13 12:21:18 -04:00
|
|
|
func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
|
2020-07-16 16:18:49 -04:00
|
|
|
ldr := ctxt.loader
|
|
|
|
|
seen := make(map[loader.Sym]struct{})
|
2020-08-13 12:21:18 -04:00
|
|
|
for _, s := range funcs {
|
2020-07-16 16:18:49 -04:00
|
|
|
if _, ok := seen[s]; !ok {
|
|
|
|
|
f(s)
|
|
|
|
|
seen[s] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fi := ldr.FuncInfo(s)
|
|
|
|
|
if !fi.Valid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fi.Preload()
|
|
|
|
|
for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ {
|
|
|
|
|
call := fi.InlTree(i).Func
|
|
|
|
|
if _, ok := seen[call]; !ok {
|
|
|
|
|
f(call)
|
|
|
|
|
seen[call] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 12:21:18 -04:00
|
|
|
// generateFuncnametab creates the function name table. Returns a map of
|
|
|
|
|
// func symbol to the name offset in runtime.funcnamtab.
|
|
|
|
|
func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
|
|
|
|
|
nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
|
|
|
|
|
|
2020-07-16 16:18:49 -04:00
|
|
|
// Write the null terminated strings.
|
|
|
|
|
writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
symtab := ctxt.loader.MakeSymbolUpdater(s)
|
2020-08-13 12:21:18 -04:00
|
|
|
for s, off := range nameOffsets {
|
2020-07-16 16:18:49 -04:00
|
|
|
symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
|
|
|
|
|
}
|
2020-06-24 14:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
2020-07-16 16:18:49 -04:00
|
|
|
// Loop through the CUs, and calculate the size needed.
|
|
|
|
|
var size int64
|
2020-08-13 12:21:18 -04:00
|
|
|
walkFuncs(ctxt, funcs, func(s loader.Sym) {
|
|
|
|
|
nameOffsets[s] = uint32(size)
|
2020-07-16 16:18:49 -04:00
|
|
|
size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
|
2020-08-13 12:21:18 -04:00
|
|
|
return nameOffsets
|
2020-06-24 14:30:16 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 12:21:18 -04:00
|
|
|
// walkFilenames walks funcs, calling a function for each filename used in each
|
|
|
|
|
// function's line table.
|
|
|
|
|
func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
|
2020-08-03 13:19:46 -04:00
|
|
|
ldr := ctxt.loader
|
|
|
|
|
|
|
|
|
|
// Loop through all functions, finding the filenames we need.
|
2020-08-13 12:21:18 -04:00
|
|
|
for _, s := range funcs {
|
2020-08-03 13:19:46 -04:00
|
|
|
fi := ldr.FuncInfo(s)
|
|
|
|
|
if !fi.Valid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fi.Preload()
|
|
|
|
|
|
|
|
|
|
cu := ldr.SymUnit(s)
|
|
|
|
|
for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
|
|
|
|
|
f(cu, fi.File(i))
|
|
|
|
|
}
|
|
|
|
|
for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
|
|
|
|
|
call := fi.InlTree(i)
|
|
|
|
|
f(cu, call.File)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
|
|
|
|
|
// of the index at which each CU begins in runtime.cutab.
|
|
|
|
|
//
|
|
|
|
|
// Function objects keep track of the files they reference to print the stack.
|
|
|
|
|
// This function creates a per-CU list of filenames if CU[M] references
|
|
|
|
|
// files[1-N], the following is generated:
|
|
|
|
|
//
|
|
|
|
|
// runtime.cutab:
|
|
|
|
|
// CU[M]
|
|
|
|
|
// offsetToFilename[0]
|
|
|
|
|
// offsetToFilename[1]
|
|
|
|
|
// ..
|
|
|
|
|
//
|
|
|
|
|
// runtime.filetab
|
|
|
|
|
// filename[0]
|
|
|
|
|
// filename[1]
|
|
|
|
|
//
|
|
|
|
|
// Looking up a filename then becomes:
|
|
|
|
|
// 0) Given a func, and filename index [K]
|
|
|
|
|
// 1) Get Func.CUIndex: M := func.cuOffset
|
|
|
|
|
// 2) Find filename offset: fileOffset := runtime.cutab[M+K]
|
|
|
|
|
// 3) Get the filename: getcstring(runtime.filetab[fileOffset])
|
2020-08-13 12:21:18 -04:00
|
|
|
func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
|
2020-08-03 13:19:46 -04:00
|
|
|
// On a per-CU basis, keep track of all the filenames we need.
|
|
|
|
|
//
|
|
|
|
|
// Note, that we store the filenames in a separate section in the object
|
|
|
|
|
// files, and deduplicate based on the actual value. It would be better to
|
|
|
|
|
// store the filenames as symbols, using content addressable symbols (and
|
|
|
|
|
// then not loading extra filenames), and just use the hash value of the
|
|
|
|
|
// symbol name to do this cataloging.
|
|
|
|
|
//
|
2021-02-17 01:48:21 +00:00
|
|
|
// TODO: Store filenames as symbols. (Note this would be easiest if you
|
2020-08-03 13:19:46 -04:00
|
|
|
// also move strings to ALWAYS using the larger content addressable hash
|
|
|
|
|
// function, and use that hash value for uniqueness testing.)
|
|
|
|
|
cuEntries := make([]goobj.CUFileIndex, len(compUnits))
|
|
|
|
|
fileOffsets := make(map[string]uint32)
|
|
|
|
|
|
|
|
|
|
// Walk the filenames.
|
|
|
|
|
// We store the total filename string length we need to load, and the max
|
|
|
|
|
// file index we've seen per CU so we can calculate how large the
|
|
|
|
|
// CU->global table needs to be.
|
|
|
|
|
var fileSize int64
|
2020-08-13 12:21:18 -04:00
|
|
|
walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
|
2020-08-03 13:19:46 -04:00
|
|
|
// Note we use the raw filename for lookup, but use the expanded filename
|
|
|
|
|
// when we save the size.
|
|
|
|
|
filename := cu.FileTable[i]
|
|
|
|
|
if _, ok := fileOffsets[filename]; !ok {
|
|
|
|
|
fileOffsets[filename] = uint32(fileSize)
|
|
|
|
|
fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the maximum file index we've seen.
|
|
|
|
|
if cuEntries[cu.PclnIndex] < i+1 {
|
|
|
|
|
cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Calculate the size of the runtime.cutab variable.
|
|
|
|
|
var totalEntries uint32
|
|
|
|
|
cuOffsets := make([]uint32, len(cuEntries))
|
|
|
|
|
for i, entries := range cuEntries {
|
|
|
|
|
// Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
|
|
|
|
|
// running total of all cu indices we've needed to store so far, not the
|
|
|
|
|
// number of bytes we've stored so far.
|
|
|
|
|
cuOffsets[i] = totalEntries
|
|
|
|
|
totalEntries += uint32(entries)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write cutab.
|
|
|
|
|
writeCutab := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
sb := ctxt.loader.MakeSymbolUpdater(s)
|
|
|
|
|
|
|
|
|
|
var off int64
|
|
|
|
|
for i, max := range cuEntries {
|
|
|
|
|
// Write the per CU LUT.
|
|
|
|
|
cu := compUnits[i]
|
|
|
|
|
for j := goobj.CUFileIndex(0); j < max; j++ {
|
|
|
|
|
fileOffset, ok := fileOffsets[cu.FileTable[j]]
|
|
|
|
|
if !ok {
|
|
|
|
|
// We're looping through all possible file indices. It's possible a file's
|
|
|
|
|
// been deadcode eliminated, and although it's a valid file in the CU, it's
|
|
|
|
|
// not needed in this binary. When that happens, use an invalid offset.
|
|
|
|
|
fileOffset = ^uint32(0)
|
|
|
|
|
}
|
|
|
|
|
off = sb.SetUint32(ctxt.Arch, off, fileOffset)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
|
|
|
|
|
|
|
|
|
|
// Write filetab.
|
|
|
|
|
writeFiletab := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
sb := ctxt.loader.MakeSymbolUpdater(s)
|
|
|
|
|
|
|
|
|
|
// Write the strings.
|
|
|
|
|
for filename, loc := range fileOffsets {
|
|
|
|
|
sb.AddStringAt(int64(loc), expandFile(filename))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state.nfiles = uint32(len(fileOffsets))
|
|
|
|
|
state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
|
|
|
|
|
|
|
|
|
|
return cuOffsets
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-12 19:26:53 -04:00
|
|
|
// generatePctab creates the runtime.pctab variable, holding all the
|
|
|
|
|
// deduplicated pcdata.
|
2020-08-13 12:21:18 -04:00
|
|
|
func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
|
2020-08-12 19:26:53 -04:00
|
|
|
ldr := ctxt.loader
|
|
|
|
|
|
|
|
|
|
// Pctab offsets of 0 are considered invalid in the runtime. We respect
|
|
|
|
|
// that by just padding a single byte at the beginning of runtime.pctab,
|
|
|
|
|
// that way no real offsets can be zero.
|
|
|
|
|
size := int64(1)
|
|
|
|
|
|
|
|
|
|
// Walk the functions, finding offset to store each pcdata.
|
|
|
|
|
seen := make(map[loader.Sym]struct{})
|
|
|
|
|
saveOffset := func(pcSym loader.Sym) {
|
|
|
|
|
if _, ok := seen[pcSym]; !ok {
|
|
|
|
|
datSize := ldr.SymSize(pcSym)
|
|
|
|
|
if datSize != 0 {
|
|
|
|
|
ldr.SetSymValue(pcSym, size)
|
|
|
|
|
} else {
|
|
|
|
|
// Invalid PC data, record as zero.
|
|
|
|
|
ldr.SetSymValue(pcSym, 0)
|
|
|
|
|
}
|
|
|
|
|
size += datSize
|
|
|
|
|
seen[pcSym] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-13 12:21:18 -04:00
|
|
|
for _, s := range funcs {
|
2020-08-12 19:26:53 -04:00
|
|
|
fi := ldr.FuncInfo(s)
|
|
|
|
|
if !fi.Valid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fi.Preload()
|
|
|
|
|
|
|
|
|
|
pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
|
|
|
|
|
for _, pcSym := range pcSyms {
|
|
|
|
|
saveOffset(pcSym)
|
|
|
|
|
}
|
|
|
|
|
for _, pcSym := range fi.Pcdata() {
|
|
|
|
|
saveOffset(pcSym)
|
|
|
|
|
}
|
|
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
saveOffset(fi.Pcinline())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: There is no reason we need a generator for this variable, and it
|
|
|
|
|
// could be moved to a carrier symbol. However, carrier symbols containing
|
|
|
|
|
// carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
|
|
|
|
|
// runtime.pctab could just be a carrier sym.
|
|
|
|
|
writePctab := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
sb := ldr.MakeSymbolUpdater(s)
|
|
|
|
|
for sym := range seen {
|
|
|
|
|
sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// numPCData returns the number of PCData syms for the FuncInfo.
|
|
|
|
|
// NB: Preload must be called on valid FuncInfos before calling this function.
|
|
|
|
|
func numPCData(fi loader.FuncInfo) uint32 {
|
|
|
|
|
if !fi.Valid() {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
numPCData := uint32(len(fi.Pcdata()))
|
|
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
if numPCData < objabi.PCDATA_InlTreeIndex+1 {
|
|
|
|
|
numPCData = objabi.PCDATA_InlTreeIndex + 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return numPCData
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Helper types for iterating pclntab.
|
|
|
|
|
type pclnSetAddr func(*loader.SymbolBuilder, *sys.Arch, int64, loader.Sym, int64) int64
|
|
|
|
|
type pclnSetUint func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64
|
|
|
|
|
|
|
|
|
|
// generateFunctab creates the runtime.functab
|
|
|
|
|
//
|
|
|
|
|
// runtime.functab contains two things:
|
|
|
|
|
//
|
|
|
|
|
// - pc->func look up table.
|
|
|
|
|
// - array of func objects, interleaved with pcdata and funcdata
|
|
|
|
|
//
|
|
|
|
|
// Because of timing in the linker, generating this table takes two passes.
|
|
|
|
|
// The first pass is executed early in the link, and it creates any needed
|
|
|
|
|
// relocations to layout the data. The pieces that need relocations are:
|
|
|
|
|
// 1) the PC->func table.
|
|
|
|
|
// 2) The entry points in the func objects.
|
|
|
|
|
// 3) The funcdata.
|
|
|
|
|
// (1) and (2) are handled in walkPCToFunc. (3) is handled in walkFuncdata.
|
|
|
|
|
//
|
|
|
|
|
// After relocations, once we know where to write things in the output buffer,
|
|
|
|
|
// we execute the second pass, which is actually writing the data.
|
|
|
|
|
func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
|
|
|
|
|
// Calculate the size of the table.
|
|
|
|
|
size, startLocations := state.calculateFunctabSize(ctxt, funcs)
|
|
|
|
|
|
|
|
|
|
// If we are internally linking a static executable, the function addresses
|
|
|
|
|
// are known, so we can just use them instead of emitting relocations. For
|
|
|
|
|
// other cases we still need to emit relocations.
|
2020-08-12 19:26:53 -04:00
|
|
|
//
|
2020-08-18 13:38:04 -04:00
|
|
|
// This boolean just helps us figure out which callback to use.
|
|
|
|
|
useSymValue := ctxt.IsExe() && ctxt.IsInternal()
|
2020-06-24 14:30:16 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
writePcln := func(ctxt *Link, s loader.Sym) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
sb := ldr.MakeSymbolUpdater(s)
|
2020-07-14 13:12:58 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Create our callbacks.
|
|
|
|
|
var setAddr pclnSetAddr
|
|
|
|
|
if useSymValue {
|
|
|
|
|
// We need to write the offset.
|
|
|
|
|
setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
|
|
|
|
|
if v := ldr.SymValue(tgt); v != 0 {
|
|
|
|
|
s.SetUint(arch, off, uint64(v+add))
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// We already wrote relocations.
|
|
|
|
|
setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { return 0 }
|
|
|
|
|
}
|
2020-06-24 14:30:16 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Write the data.
|
|
|
|
|
writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
|
|
|
|
|
writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
|
|
|
|
|
state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
|
|
|
|
|
}
|
2020-08-13 12:21:18 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
|
2020-06-24 14:30:16 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Create the relocations we need.
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
sb := ldr.MakeSymbolUpdater(state.pclntab)
|
|
|
|
|
|
|
|
|
|
var setAddr pclnSetAddr
|
|
|
|
|
if useSymValue {
|
|
|
|
|
// If we should use the symbol value, and we don't have one, write a relocation.
|
|
|
|
|
setAddr = func(sb *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
|
|
|
|
|
if v := ldr.SymValue(tgt); v == 0 {
|
|
|
|
|
sb.SetAddrPlus(arch, off, tgt, add)
|
2020-04-15 22:23:41 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
return 0
|
2020-04-15 22:23:41 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
} else {
|
|
|
|
|
// If we're externally linking, write a relocation.
|
|
|
|
|
setAddr = (*loader.SymbolBuilder).SetAddrPlus
|
2020-04-15 22:23:41 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
setUintNOP := func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 { return 0 }
|
|
|
|
|
writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, setUintNOP)
|
|
|
|
|
if !useSymValue {
|
|
|
|
|
// Generate relocations for funcdata when externally linking.
|
|
|
|
|
state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, setUintNOP)
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 22:23:41 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// funcData returns the funcdata and offsets for the FuncInfo.
|
|
|
|
|
// The funcdata and offsets are written into runtime.functab after each func
|
|
|
|
|
// object. This is a helper function to make querying the FuncInfo object
|
|
|
|
|
// cleaner.
|
|
|
|
|
//
|
|
|
|
|
// Note, the majority of fdOffsets are 0, meaning there is no offset between
|
|
|
|
|
// the compiler's generated symbol, and what the runtime needs. They are
|
|
|
|
|
// plumbed through for no loss of generality.
|
|
|
|
|
//
|
|
|
|
|
// NB: Preload must be called on the FuncInfo before calling.
|
|
|
|
|
// NB: fdSyms and fdOffs are used as scratch space.
|
|
|
|
|
func funcData(fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym, fdOffs []int64) ([]loader.Sym, []int64) {
|
|
|
|
|
fdSyms, fdOffs = fdSyms[:0], fdOffs[:0]
|
|
|
|
|
if fi.Valid() {
|
|
|
|
|
numOffsets := int(fi.NumFuncdataoff())
|
|
|
|
|
for i := 0; i < numOffsets; i++ {
|
|
|
|
|
fdOffs = append(fdOffs, fi.Funcdataoff(i))
|
|
|
|
|
}
|
|
|
|
|
fdSyms = fi.Funcdata(fdSyms)
|
|
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
|
|
|
|
|
fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
|
|
|
|
|
fdOffs = append(fdOffs, make([]int64, objabi.FUNCDATA_InlTree+1-len(fdOffs))...)
|
|
|
|
|
}
|
|
|
|
|
fdSyms[objabi.FUNCDATA_InlTree] = inlSym
|
2020-02-16 16:18:04 -05:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
}
|
|
|
|
|
return fdSyms, fdOffs
|
|
|
|
|
}
|
2020-02-16 16:18:04 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// calculateFunctabSize calculates the size of the pclntab, and the offsets in
|
|
|
|
|
// the output buffer for individual func entries.
|
|
|
|
|
func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
startLocations := make([]uint32, len(funcs))
|
|
|
|
|
|
|
|
|
|
// Allocate space for the pc->func table. This structure consists of a pc
|
|
|
|
|
// and an offset to the func structure. After that, we have a single pc
|
|
|
|
|
// value that marks the end of the last function in the binary.
|
|
|
|
|
size := int64(int(state.nfunc)*2*ctxt.Arch.PtrSize + ctxt.Arch.PtrSize)
|
|
|
|
|
|
|
|
|
|
// Now find the space for the func objects. We do this in a running manner,
|
|
|
|
|
// so that we can find individual starting locations, and because funcdata
|
|
|
|
|
// requires alignment.
|
|
|
|
|
for i, s := range funcs {
|
|
|
|
|
size = Rnd(size, int64(ctxt.Arch.PtrSize))
|
|
|
|
|
startLocations[i] = uint32(size)
|
2020-04-07 17:08:00 -04:00
|
|
|
fi := ldr.FuncInfo(s)
|
2020-08-18 13:38:04 -04:00
|
|
|
size += int64(state.funcSize)
|
2020-04-07 17:08:00 -04:00
|
|
|
if fi.Valid() {
|
|
|
|
|
fi.Preload()
|
2020-08-18 13:38:04 -04:00
|
|
|
numFuncData := int(fi.NumFuncdataoff())
|
|
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
if numFuncData < objabi.FUNCDATA_InlTree+1 {
|
|
|
|
|
numFuncData = objabi.FUNCDATA_InlTree + 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
size += int64(numPCData(fi) * 4)
|
|
|
|
|
if numFuncData > 0 { // Func data is aligned.
|
|
|
|
|
size = Rnd(size, int64(ctxt.Arch.PtrSize))
|
2020-04-07 17:08:00 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
size += int64(numFuncData * ctxt.Arch.PtrSize)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
return size, startLocations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// writePcToFunc writes the PC->func lookup table.
|
|
|
|
|
// This function walks the pc->func lookup table, executing callbacks
|
|
|
|
|
// to generate relocations and writing the values for the table.
|
|
|
|
|
func writePcToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
var prevFunc loader.Sym
|
|
|
|
|
prevSect := ldr.SymSect(funcs[0])
|
|
|
|
|
funcIndex := 0
|
|
|
|
|
for i, s := range funcs {
|
|
|
|
|
if thisSect := ldr.SymSect(s); thisSect != prevSect {
|
|
|
|
|
// With multiple text sections, there may be a hole here in the
|
|
|
|
|
// address space. We use an invalid funcoff value to mark the hole.
|
|
|
|
|
// See also runtime/symtab.go:findfunc
|
|
|
|
|
prevFuncSize := int64(ldr.SymSize(prevFunc))
|
|
|
|
|
setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
|
|
|
|
|
setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), ^uint64(0))
|
|
|
|
|
funcIndex++
|
|
|
|
|
prevSect = thisSect
|
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
prevFunc = s
|
|
|
|
|
// TODO: We don't actually need these relocations, provided we go to a
|
|
|
|
|
// module->func look-up-table like we do for filenames. We could have a
|
|
|
|
|
// single relocation for the module, and have them all laid out as
|
|
|
|
|
// offsets from the beginning of that module.
|
|
|
|
|
setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), s, 0)
|
|
|
|
|
setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), uint64(startLocations[i]))
|
|
|
|
|
funcIndex++
|
|
|
|
|
|
|
|
|
|
// Write the entry location.
|
|
|
|
|
setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0)
|
|
|
|
|
}
|
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Final entry of table is just end pc.
|
|
|
|
|
setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc))
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// writeFuncData writes the funcdata tables.
|
|
|
|
|
//
|
|
|
|
|
// This function executes a callback for each funcdata needed in
|
|
|
|
|
// runtime.functab. It should be called once for internally linked static
|
|
|
|
|
// binaries, or twice (once to generate the needed relocations) for other
|
|
|
|
|
// build modes.
|
|
|
|
|
//
|
|
|
|
|
// Note the output of this function is interwoven with writeFuncs, but this is
|
|
|
|
|
// a separate function, because it's needed in different passes in
|
|
|
|
|
// generateFunctab.
|
|
|
|
|
func (state *pclntab) writeFuncData(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
funcdata, funcdataoff := []loader.Sym{}, []int64{}
|
|
|
|
|
for i, s := range funcs {
|
|
|
|
|
fi := ldr.FuncInfo(s)
|
|
|
|
|
if !fi.Valid() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fi.Preload()
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// funcdata, must be pointer-aligned and we're only int32-aligned.
|
|
|
|
|
// Missing funcdata will be 0 (nil pointer).
|
|
|
|
|
funcdata, funcdataoff := funcData(fi, inlSyms[s], funcdata, funcdataoff)
|
|
|
|
|
if len(funcdata) > 0 {
|
|
|
|
|
off := int64(startLocations[i] + state.funcSize + numPCData(fi)*4)
|
|
|
|
|
off = Rnd(off, int64(ctxt.Arch.PtrSize))
|
|
|
|
|
for j := range funcdata {
|
|
|
|
|
dataoff := off + int64(ctxt.Arch.PtrSize*j)
|
|
|
|
|
if funcdata[j] == 0 {
|
|
|
|
|
setUint(sb, ctxt.Arch, dataoff, uint64(funcdataoff[j]))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// TODO: Does this need deduping?
|
|
|
|
|
setAddr(sb, ctxt.Arch, dataoff, funcdata[j], funcdataoff[j])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-14 13:24:21 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// writeFuncs writes the func structures and pcdata to runtime.functab.
|
|
|
|
|
func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
deferReturnSym := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
|
|
|
|
|
funcdata, funcdataoff := []loader.Sym{}, []int64{}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Write the individual func objects.
|
|
|
|
|
for i, s := range funcs {
|
|
|
|
|
fi := ldr.FuncInfo(s)
|
|
|
|
|
if fi.Valid() {
|
|
|
|
|
fi.Preload()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// Note we skip the space for the entry value -- that's handled inn
|
|
|
|
|
// walkPCToFunc. We don't write it here, because it might require a
|
|
|
|
|
// relocation.
|
|
|
|
|
off := startLocations[i] + uint32(ctxt.Arch.PtrSize) // entry
|
2015-02-27 22:57:28 -05:00
|
|
|
|
|
|
|
|
// name int32
|
2020-08-13 12:21:18 -04:00
|
|
|
nameoff, ok := nameOffsets[s]
|
2020-07-16 16:18:49 -04:00
|
|
|
if !ok {
|
|
|
|
|
panic("couldn't find function name offset")
|
|
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
|
2015-02-27 22:57:28 -05:00
|
|
|
|
|
|
|
|
// args int32
|
|
|
|
|
// TODO: Move into funcinfo.
|
2016-04-11 22:19:34 +03:00
|
|
|
args := uint32(0)
|
2020-04-07 17:08:00 -04:00
|
|
|
if fi.Valid() {
|
|
|
|
|
args = uint32(fi.Args())
|
2016-04-11 22:19:34 +03:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args))
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2018-09-11 15:14:28 -07:00
|
|
|
// deferreturn
|
2020-08-13 12:21:18 -04:00
|
|
|
deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn))
|
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
|
|
|
|
2015-02-27 22:57:28 -05:00
|
|
|
// pcdata
|
2020-08-12 19:26:53 -04:00
|
|
|
if fi.Valid() {
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
|
|
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
|
|
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
|
2020-08-12 19:26:53 -04:00
|
|
|
} else {
|
|
|
|
|
off += 12
|
|
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(numPCData(fi))))
|
2018-09-11 15:14:28 -07:00
|
|
|
|
2020-08-03 13:19:46 -04:00
|
|
|
// Store the offset to compilation unit's file table.
|
|
|
|
|
cuIdx := ^uint32(0)
|
2020-07-23 15:15:26 -04:00
|
|
|
if cu := ldr.SymUnit(s); cu != nil {
|
2020-08-03 13:19:46 -04:00
|
|
|
cuIdx = cuOffsets[cu.PclnIndex]
|
2020-07-23 15:15:26 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx))
|
2020-07-23 15:15:26 -04:00
|
|
|
|
2018-09-11 15:14:28 -07:00
|
|
|
// funcID uint8
|
2020-07-21 15:53:30 -04:00
|
|
|
var funcID objabi.FuncID
|
|
|
|
|
if fi.Valid() {
|
|
|
|
|
funcID = fi.FuncID()
|
2018-09-11 15:14:28 -07:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
|
2018-09-11 15:14:28 -07:00
|
|
|
|
cmd/asm, cmd/link, runtime: introduce FuncInfo flag bits
The runtime traceback code has its own definition of which functions
mark the top frame of a stack, separate from the TOPFRAME bits that
exist in the assembly and are passed along in DWARF information.
It's error-prone and redundant to have two different sources of truth.
This CL provides the actual TOPFRAME bits to the runtime, so that
the runtime can use those bits instead of reinventing its own category.
This CL also adds a new bit, SPWRITE, which marks functions that
write directly to SP (anything but adding and subtracting constants).
Such functions must stop a traceback, because the traceback has no
way to rederive the SP on entry. Again, the runtime has its own definition
which is mostly correct, but also missing some functions. During ordinary
goroutine context switches, such functions do not appear on the stack,
so the incompleteness in the runtime usually doesn't matter.
But profiling signals can arrive at any moment, and the runtime may
crash during traceback if it attempts to unwind an SP-writing frame
and gets out-of-sync with the actual stack. The runtime contains code
to try to detect likely candidates but again it is incomplete.
Deriving the SPWRITE bit automatically from the actual assembly code
provides the complete truth, and passing it to the runtime lets the
runtime use it.
This CL is part of a stack adding windows/arm64
support (#36439), intended to land in the Go 1.17 cycle.
This CL is, however, not windows/arm64-specific.
It is cleanup meant to make the port (and future ports) easier.
Change-Id: I227f53b23ac5b3dabfcc5e8ee3f00df4e113cf58
Reviewed-on: https://go-review.googlesource.com/c/go/+/288800
Trust: Russ Cox <rsc@golang.org>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-01-28 15:21:33 -05:00
|
|
|
// flag uint8
|
|
|
|
|
var flag objabi.FuncFlag
|
|
|
|
|
if fi.Valid() {
|
|
|
|
|
flag = fi.FuncFlag()
|
|
|
|
|
}
|
|
|
|
|
off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(flag)))
|
|
|
|
|
|
|
|
|
|
off += 1 // pad
|
2020-08-03 13:19:46 -04:00
|
|
|
|
2018-09-11 15:14:28 -07:00
|
|
|
// nfuncdata must be the final entry.
|
2020-08-18 13:38:04 -04:00
|
|
|
funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff)
|
|
|
|
|
off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
|
2020-08-12 19:26:53 -04:00
|
|
|
|
|
|
|
|
// Output the pcdata.
|
|
|
|
|
if fi.Valid() {
|
2020-08-18 13:38:04 -04:00
|
|
|
for j, pcSym := range fi.Pcdata() {
|
|
|
|
|
sb.SetUint32(ctxt.Arch, int64(off+uint32(j*4)), uint32(ldr.SymValue(pcSym)))
|
2020-08-12 19:26:53 -04:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
if fi.NumInlTree() > 0 {
|
|
|
|
|
sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
|
2020-08-12 19:26:53 -04:00
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2020-08-18 13:38:04 -04:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// pclntab initializes the pclntab symbol with
|
|
|
|
|
// runtime function and file name information.
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
// pclntab generates the pcln table for the link output.
|
|
|
|
|
func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
|
|
|
|
|
// Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
|
|
|
|
|
// layout and data has changed since that time.
|
|
|
|
|
//
|
|
|
|
|
// As of August 2020, here's the layout of pclntab:
|
|
|
|
|
//
|
|
|
|
|
// .gopclntab/__gopclntab [elf/macho section]
|
|
|
|
|
// runtime.pclntab
|
|
|
|
|
// Carrier symbol for the entire pclntab section.
|
|
|
|
|
//
|
|
|
|
|
// runtime.pcheader (see: runtime/symtab.go:pcHeader)
|
|
|
|
|
// 8-byte magic
|
|
|
|
|
// nfunc [thearch.ptrsize bytes]
|
|
|
|
|
// offset to runtime.funcnametab from the beginning of runtime.pcheader
|
|
|
|
|
// offset to runtime.pclntab_old from beginning of runtime.pcheader
|
|
|
|
|
//
|
|
|
|
|
// runtime.funcnametab
|
|
|
|
|
// []list of null terminated function names
|
|
|
|
|
//
|
|
|
|
|
// runtime.cutab
|
|
|
|
|
// for i=0..#CUs
|
|
|
|
|
// for j=0..#max used file index in CU[i]
|
|
|
|
|
// uint32 offset into runtime.filetab for the filename[j]
|
|
|
|
|
//
|
|
|
|
|
// runtime.filetab
|
|
|
|
|
// []null terminated filename strings
|
|
|
|
|
//
|
|
|
|
|
// runtime.pctab
|
|
|
|
|
// []byte of deduplicated pc data.
|
|
|
|
|
//
|
|
|
|
|
// runtime.functab
|
|
|
|
|
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
|
|
|
|
|
// end PC [thearch.ptrsize bytes]
|
|
|
|
|
// func structures, pcdata offsets, func data.
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
state, compUnits, funcs := makePclntab(ctxt, container)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
ldr := ctxt.loader
|
|
|
|
|
state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
|
|
|
|
|
ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
|
|
|
|
|
ldr.SetAttrReachable(state.carrier, true)
|
2020-12-14 18:52:13 -05:00
|
|
|
setCarrierSym(sym.SPCLNTAB, state.carrier)
|
2020-04-07 17:08:00 -04:00
|
|
|
|
2020-08-18 13:38:04 -04:00
|
|
|
state.generatePCHeader(ctxt)
|
|
|
|
|
nameOffsets := state.generateFuncnametab(ctxt, funcs)
|
|
|
|
|
cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
|
|
|
|
|
state.generatePctab(ctxt, funcs)
|
|
|
|
|
inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
|
|
|
|
|
state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
|
2020-04-10 10:30:27 -04:00
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
return state
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
cmd/link: set runtime.GOROOT default during link
Suppose you build the Go toolchain in directory A,
move the whole thing to directory B, and then use
it from B to build a new program hello.exe, and then
run hello.exe, and hello.exe crashes with a stack
trace into the standard library.
Long ago, you'd have seen hello.exe print file names
in the A directory tree, even though the files had moved
to the B directory tree. About two years ago we changed
the compiler to write down these files with the name
"$GOROOT" (that literal string) instead of A, so that the
final link from B could replace "$GOROOT" with B,
so that hello.exe's crash would show the correct source
file paths in the stack trace. (golang.org/cl/18200)
Now suppose that you do the same thing but hello.exe
doesn't crash: it prints fmt.Println(runtime.GOROOT()).
And you run hello.exe after clearing $GOROOT from the
environment.
Long ago, you'd have seen hello.exe print A instead of B.
Before this CL, you'd still see hello.exe print A instead of B.
This case is the one instance where a moved toolchain
still divulges its origin. Not anymore. After this CL, hello.exe
will print B, because the linker sets runtime/internal/sys.DefaultGoroot
with the effective GOROOT from link time.
This makes the default result of runtime.GOROOT once again
match the file names recorded in the binary, after two years
of divergence.
With that cleared up, we can reintroduce GOROOT into the
link action ID and also reenable TestExecutableGOROOT/RelocatedExe.
When $GOROOT_FINAL is set during link, it is used
in preference to $GOROOT, as always, but it was easier
to explain the behavior above without introducing that
complication.
Fixes #22155.
Fixes #20284.
Fixes #22475.
Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016
Reviewed-on: https://go-review.googlesource.com/86835
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 11:59:29 -05:00
|
|
|
func gorootFinal() string {
|
|
|
|
|
root := objabi.GOROOT
|
|
|
|
|
if final := os.Getenv("GOROOT_FINAL"); final != "" {
|
|
|
|
|
root = final
|
|
|
|
|
}
|
|
|
|
|
return root
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-29 10:16:40 -05:00
|
|
|
func expandGoroot(s string) string {
|
|
|
|
|
const n = len("$GOROOT")
|
|
|
|
|
if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
|
cmd/link: set runtime.GOROOT default during link
Suppose you build the Go toolchain in directory A,
move the whole thing to directory B, and then use
it from B to build a new program hello.exe, and then
run hello.exe, and hello.exe crashes with a stack
trace into the standard library.
Long ago, you'd have seen hello.exe print file names
in the A directory tree, even though the files had moved
to the B directory tree. About two years ago we changed
the compiler to write down these files with the name
"$GOROOT" (that literal string) instead of A, so that the
final link from B could replace "$GOROOT" with B,
so that hello.exe's crash would show the correct source
file paths in the stack trace. (golang.org/cl/18200)
Now suppose that you do the same thing but hello.exe
doesn't crash: it prints fmt.Println(runtime.GOROOT()).
And you run hello.exe after clearing $GOROOT from the
environment.
Long ago, you'd have seen hello.exe print A instead of B.
Before this CL, you'd still see hello.exe print A instead of B.
This case is the one instance where a moved toolchain
still divulges its origin. Not anymore. After this CL, hello.exe
will print B, because the linker sets runtime/internal/sys.DefaultGoroot
with the effective GOROOT from link time.
This makes the default result of runtime.GOROOT once again
match the file names recorded in the binary, after two years
of divergence.
With that cleared up, we can reintroduce GOROOT into the
link action ID and also reenable TestExecutableGOROOT/RelocatedExe.
When $GOROOT_FINAL is set during link, it is used
in preference to $GOROOT, as always, but it was easier
to explain the behavior above without introducing that
complication.
Fixes #22155.
Fixes #20284.
Fixes #22475.
Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016
Reviewed-on: https://go-review.googlesource.com/86835
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 11:59:29 -05:00
|
|
|
return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:]))
|
2015-12-29 10:16:40 -05:00
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-27 22:57:28 -05:00
|
|
|
const (
|
|
|
|
|
BUCKETSIZE = 256 * MINFUNC
|
|
|
|
|
SUBBUCKETS = 16
|
|
|
|
|
SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
|
|
|
|
|
NOIDX = 0x7fffffff
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// findfunctab generates a lookup table to quickly find the containing
|
2016-03-01 23:21:55 +00:00
|
|
|
// function for a pc. See src/runtime/symtab.go:findfunc for details.
|
2020-07-14 13:12:58 -04:00
|
|
|
func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) {
|
2020-04-10 10:30:27 -04:00
|
|
|
ldr := ctxt.loader
|
2015-02-27 22:57:28 -05:00
|
|
|
|
|
|
|
|
// find min and max address
|
2020-05-15 18:35:05 -04:00
|
|
|
min := ldr.SymValue(ctxt.Textp[0])
|
|
|
|
|
lastp := ctxt.Textp[len(ctxt.Textp)-1]
|
2020-04-10 10:30:27 -04:00
|
|
|
max := ldr.SymValue(lastp) + ldr.SymSize(lastp)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
|
|
|
|
// for each subbucket, compute the minimum of all symbol indexes
|
|
|
|
|
// that map to that subbucket.
|
2015-03-02 12:35:15 -05:00
|
|
|
n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-06-22 11:11:12 -04:00
|
|
|
nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
|
|
|
|
|
|
|
|
|
|
size := 4*int64(nbuckets) + int64(n)
|
|
|
|
|
|
|
|
|
|
writeFindFuncTab := func(_ *Link, s loader.Sym) {
|
|
|
|
|
t := ldr.MakeSymbolUpdater(s)
|
|
|
|
|
|
|
|
|
|
indexes := make([]int32, n)
|
|
|
|
|
for i := int32(0); i < n; i++ {
|
|
|
|
|
indexes[i] = NOIDX
|
2016-04-19 14:02:21 -04:00
|
|
|
}
|
2020-06-22 11:11:12 -04:00
|
|
|
idx := int32(0)
|
|
|
|
|
for i, s := range ctxt.Textp {
|
|
|
|
|
if !emitPcln(ctxt, s, container) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
p := ldr.SymValue(s)
|
|
|
|
|
var e loader.Sym
|
2016-04-19 14:02:21 -04:00
|
|
|
i++
|
2020-06-22 11:11:12 -04:00
|
|
|
if i < len(ctxt.Textp) {
|
|
|
|
|
e = ctxt.Textp[i]
|
|
|
|
|
}
|
|
|
|
|
for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) {
|
|
|
|
|
e = ctxt.Textp[i]
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
q := max
|
|
|
|
|
if e != 0 {
|
|
|
|
|
q = ldr.SymValue(e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
|
|
|
|
|
for ; p < q; p += SUBBUCKETSIZE {
|
|
|
|
|
i = int((p - min) / SUBBUCKETSIZE)
|
|
|
|
|
if indexes[i] > idx {
|
|
|
|
|
indexes[i] = idx
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-06-22 11:11:12 -04:00
|
|
|
i = int((q - 1 - min) / SUBBUCKETSIZE)
|
2015-02-27 22:57:28 -05:00
|
|
|
if indexes[i] > idx {
|
|
|
|
|
indexes[i] = idx
|
|
|
|
|
}
|
2020-06-22 11:11:12 -04:00
|
|
|
idx++
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2020-06-22 11:11:12 -04:00
|
|
|
// fill in table
|
|
|
|
|
for i := int32(0); i < nbuckets; i++ {
|
|
|
|
|
base := indexes[i*SUBBUCKETS]
|
|
|
|
|
if base == NOIDX {
|
2016-09-17 09:39:33 -04:00
|
|
|
Errorf(nil, "hole in findfunctab")
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2020-06-22 11:11:12 -04:00
|
|
|
t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
|
|
|
|
|
for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
|
|
|
|
|
idx = indexes[i*SUBBUCKETS+j]
|
|
|
|
|
if idx == NOIDX {
|
|
|
|
|
Errorf(nil, "hole in findfunctab")
|
|
|
|
|
}
|
|
|
|
|
if idx-base >= 256 {
|
|
|
|
|
Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2020-06-22 11:11:12 -04:00
|
|
|
t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-22 11:11:12 -04:00
|
|
|
|
2020-07-14 13:12:58 -04:00
|
|
|
state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab)
|
|
|
|
|
ldr.SetAttrReachable(state.findfunctab, true)
|
|
|
|
|
ldr.SetAttrLocal(state.findfunctab, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// findContainerSyms returns a bitmap, indexed by symbol number, where there's
|
|
|
|
|
// a 1 for every container symbol.
|
|
|
|
|
func (ctxt *Link) findContainerSyms() loader.Bitmap {
|
|
|
|
|
ldr := ctxt.loader
|
|
|
|
|
container := loader.MakeBitmap(ldr.NSym())
|
|
|
|
|
// Find container symbols and mark them as such.
|
|
|
|
|
for _, s := range ctxt.Textp {
|
|
|
|
|
outer := ldr.OuterSym(s)
|
|
|
|
|
if outer != 0 {
|
|
|
|
|
container.Set(outer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return container
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|