Use an offset from go.func.* instead.
This removes the last relocation from funcdata symbols,
which lets us simplify that code.
size before after Δ %
addr2line
3683218 3680706 -2512 -0.068%
api
4951074 4944850 -6224 -0.126%
asm
4744258 4757586 +13328 +0.281%
buildid
2419986 2418546 -1440 -0.060%
cgo
4218306 4197346 -20960 -0.497%
compile
22132066 22076882 -55184 -0.249%
cover
4432834 4411362 -21472 -0.484%
dist
3111202 3091346 -19856 -0.638%
doc
3583602 3563234 -20368 -0.568%
fix
3023922 3020658 -3264 -0.108%
link
6188034 6164642 -23392 -0.378%
nm
3665826 3646818 -19008 -0.519%
objdump
4015234 4012450 -2784 -0.069%
pack
2155010 2153554 -1456 -0.068%
pprof
13044178 13011522 -32656 -0.250%
test2json
2402146 2383906 -18240 -0.759%
trace
9765410 9736514 -28896 -0.296%
vet
6681250 6655058 -26192 -0.392%
total
104217556 103926980 -290576 -0.279%
relocs before after Δ %
addr2line 25563 25066 -497 -1.944%
api 18409 17176 -1233 -6.698%
asm 18903 18271 -632 -3.343%
buildid 9513 9233 -280 -2.943%
cgo 17103 16222 -881 -5.151%
compile 64825 60421 -4404 -6.794%
cover 19464 18479 -985 -5.061%
dist 10798 10135 -663 -6.140%
doc 13503 12735 -768 -5.688%
fix 11465 10820 -645 -5.626%
link 23214 21849 -1365 -5.880%
nm 25480 24987 -493 -1.935%
objdump 26610 26057 -553 -2.078%
pack 7951 7665 -286 -3.597%
pprof 63964 60761 -3203 -5.008%
test2json 8735 8389 -346 -3.961%
trace 39639 37180 -2459 -6.203%
vet 25970 24044 -1926 -7.416%
total 431108 409489 -21619 -5.015%
Change-Id: I43c26196a008da6d1cb3a782eea2f428778bd569
Reviewed-on: https://go-review.googlesource.com/c/go/+/353138
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
}
off = objw.Uint32(x, off, uint32(sz))
off = objw.Uint32(x, off, uint32(ptrdata))
- off = objw.SymPtr(x, off, lsym, 0)
+ off = objw.SymPtrOff(x, off, lsym)
}
if base.Flag.Live != 0 {
// the relro data.
isRelro = true
}
+ case sym.SGOFUNC:
+ // The only SGOFUNC symbols that contain relocations are .stkobj,
+ // and their relocations are of type objabi.R_ADDROFF,
+ // which always get resolved during linking.
+ isRelro = false
}
if isRelro {
state.setSymType(s, symnrelro)
ldr := ctxt.loader
deferReturnSym := ldr.Lookup("runtime.deferreturn", abiInternalVer)
gofunc := ldr.Lookup("go.func.*", 0)
- gofuncrel := ldr.Lookup("go.funcrel.*", 0)
+ gofuncBase := ldr.SymValue(gofunc)
textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
funcdata := []loader.Sym{}
var pcsp, pcfile, pcline, pcinline loader.Sym
continue
}
- outer := ldr.OuterSym(fdsym)
- if outer == 0 {
- panic(fmt.Sprintf("no carrier sym for symbol %s (funcdata %s#%d)", ldr.SymName(fdsym), ldr.SymName(s), j))
+ if outer := ldr.OuterSym(fdsym); outer != gofunc {
+ 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)))
}
- rel := uint32(ldr.SymValue(fdsym) - ldr.SymValue(outer))
- // Record gofunc vs gofuncrel in bottom bit. See runtime/symtab.go:funcdata.
- // TODO: The only symbols that in gofuncrel are .stkobj symbols.
- // Remove those relocations, and simplify this.
- rel <<= 1
- switch outer {
- case gofunc:
- case gofuncrel:
- rel |= 1
- default:
- panic(fmt.Sprintf("expected symbol %s (funcdata %s#%d) to be placed in go.func.* or go.funcrel.*, got %s (%d)",
- ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer), outer))
- }
- sb.SetUint32(ctxt.Arch, int64(dataoff), rel)
+ sb.SetUint32(ctxt.Arch, int64(dataoff), uint32(ldr.SymValue(fdsym)-gofuncBase))
}
}
}
strings.HasSuffix(name, ".args_stackmap"),
strings.HasSuffix(name, ".stkobj"):
ldr.SetAttrNotInSymbolTable(s, true)
- if ctxt.UseRelro() && strings.HasSuffix(name, ".stkobj") {
- symGroupType[s] = sym.SGOFUNCRELRO
- ldr.SetCarrierSym(s, symgofuncrel)
- } else {
- symGroupType[s] = sym.SGOFUNC
- ldr.SetCarrierSym(s, symgofunc)
- }
+ symGroupType[s] = sym.SGOFUNC
+ ldr.SetCarrierSym(s, symgofunc)
if ctxt.Debugvlog != 0 {
align := ldr.SymAlign(s)
liveness += (ldr.SymSize(s) + int64(align) - 1) &^ (int64(align) - 1)
moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.gcbss", 0))
moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.types", 0))
moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.etypes", 0))
+ moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.rodata", 0))
moduledata.AddAddr(ctxt.Arch, ldr.Lookup("go.func.*", 0))
- if gofuncrel := ldr.Lookup("go.funcrel.*", 0); gofuncrel != 0 {
- moduledata.AddAddr(ctxt.Arch, gofuncrel)
- } else {
- moduledata.AddUint(ctxt.Arch, 0)
- }
if ctxt.IsAIX() && ctxt.IsExternal() {
// Add R_XCOFFREF relocation to prevent ld's garbage collection of
println()
printunlock()
}
- gcdata := r.gcdata
+ gcdata := r.gcdata()
var s *mspan
if r.useGCProg() {
// This path is pretty unlikely, an object large enough
// varp is 0 for defers, where there are no locals.
// In that case, there can't be a pointer to its args, either.
// (And all args would be scanned above anyway.)
- for i, obj := range objs {
+ for i := range objs {
+ obj := &objs[i]
off := obj.off
base := frame.varp // locals base pointer
if off >= 0 {
if stackTraceDebug {
println("stkobj at", hex(ptr), "of size", obj.size)
}
- state.addObject(ptr, &objs[i])
+ state.addObject(ptr, obj)
}
}
}
modulesinit() // provides activeModules
typelinksinit() // uses maps, activeModules
itabsinit() // uses activeModules
+ stkobjinit() // must run before GC starts
sigsave(&_g_.m.sigmask)
initSigmask = _g_.m.sigmask
// Adjust pointers in all stack objects (whether they are live or not).
// See comments in mgcmark.go:scanframeworker.
if frame.varp != 0 {
- for _, obj := range objs {
+ for i := range objs {
+ obj := &objs[i]
off := obj.off
base := frame.varp // locals base pointer
if off >= 0 {
continue
}
ptrdata := obj.ptrdata()
- gcdata := obj.gcdata
+ gcdata := obj.gcdata()
var s *mspan
if obj.useGCProg() {
// See comments in mgcmark.go:scanstack
// We don't actually use argmap in this case, but we need to fake the stack object
// record for these frames which contain an internal/abi.RegArgs at a hard-coded offset.
// This offset matches the assembly code on amd64 and arm64.
- objs = methodValueCallFrameObjs
+ objs = methodValueCallFrameObjs[:]
} else {
p := funcdata(f, _FUNCDATA_StackObjects)
if p != nil {
return
}
-var (
- abiRegArgsEface interface{} = abi.RegArgs{}
- abiRegArgsType *_type = efaceOf(&abiRegArgsEface)._type
- methodValueCallFrameObjs = []stackObjectRecord{
- {
- off: -int32(alignUp(abiRegArgsType.size, 8)), // It's always the highest address local.
- size: int32(abiRegArgsType.size),
- _ptrdata: int32(abiRegArgsType.ptrdata),
- gcdata: abiRegArgsType.gcdata,
- },
- }
-)
+var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit
-func init() {
+func stkobjinit() {
+ var abiRegArgsEface interface{} = abi.RegArgs{}
+ abiRegArgsType := efaceOf(&abiRegArgsEface)._type
if abiRegArgsType.kind&kindGCProg != 0 {
throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs")
}
+ // Set methodValueCallFrameObjs[0].gcdataoff so that
+ // stackObjectRecord.gcdata() will work correctly with it.
+ ptr := uintptr(unsafe.Pointer(&methodValueCallFrameObjs[0]))
+ var mod *moduledata
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ if datap.gofunc <= ptr && ptr < datap.end {
+ mod = datap
+ break
+ }
+ }
+ if mod == nil {
+ throw("methodValueCallFrameObjs is not in a module")
+ }
+ methodValueCallFrameObjs[0] = stackObjectRecord{
+ off: -int32(alignUp(abiRegArgsType.size, 8)), // It's always the highest address local.
+ size: int32(abiRegArgsType.size),
+ _ptrdata: int32(abiRegArgsType.ptrdata),
+ gcdataoff: uint32(uintptr(unsafe.Pointer(abiRegArgsType.gcdata)) - mod.rodata),
+ }
}
// A stackObjectRecord is generated by the compiler for each stack object in a stack frame.
// offset in frame
// if negative, offset from varp
// if non-negative, offset from argp
- off int32
- size int32
- _ptrdata int32 // ptrdata, or -ptrdata is GC prog is used
- gcdata *byte // pointer map or GC prog of the type
+ off int32
+ size int32
+ _ptrdata int32 // ptrdata, or -ptrdata is GC prog is used
+ gcdataoff uint32 // offset to gcdata from moduledata.rodata
}
func (r *stackObjectRecord) useGCProg() bool {
return uintptr(x)
}
+// gcdata returns pointer map or GC prog of the type.
+func (r *stackObjectRecord) gcdata() *byte {
+ ptr := uintptr(unsafe.Pointer(r))
+ var mod *moduledata
+ for datap := &firstmoduledata; datap != nil; datap = datap.next {
+ if datap.gofunc <= ptr && ptr < datap.end {
+ mod = datap
+ break
+ }
+ }
+ // If you get a panic here due to a nil mod,
+ // you may have made a copy of a stackObjectRecord.
+ // You must use the original pointer.
+ res := mod.rodata + uintptr(r.gcdataoff)
+ return (*byte)(unsafe.Pointer(res))
+}
+
// This is exported as ABI0 via linkname so obj can call it.
//
//go:nosplit
noptrbss, enoptrbss uintptr
end, gcdata, gcbss uintptr
types, etypes uintptr
- gofunc, gofuncrel uintptr // go.func.*, go.funcrel.*
+ rodata uintptr
+ gofunc uintptr // go.func.*
textsectmap []textsect
typelinks []int32 // offsets from types
if off == ^uint32(0) {
return nil
}
- base := f.datap.gofunc
- if off&1 != 0 {
- base = f.datap.gofuncrel
- }
- return unsafe.Pointer(base + uintptr(off>>1))
+ return unsafe.Pointer(f.datap.gofunc + uintptr(off))
}
// step advances to the next pc, value pair in the encoded table.