mirror of
https://github.com/golang/go
synced 2024-11-05 20:06:10 -07:00
runtime: consolidate stkframe and its methods into stkframe.go
The stkframe struct and its methods are strewn across different source files. Since they actually have a pretty coherent theme at this point, migrate it all into a new file, stkframe.go. There are no code changes in this CL. For #54466, albeit rather indirectly. Change-Id: Ibe53fc4b1106d131005e1c9d491be838a8f14211 Reviewed-on: https://go-review.googlesource.com/c/go/+/424516 Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Austin Clements <austin@google.com> Auto-Submit: Austin Clements <austin@google.com>
This commit is contained in:
parent
b91e373729
commit
35026f3732
@ -985,55 +985,6 @@ type _panic struct {
|
|||||||
goexit bool
|
goexit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A stkframe holds information about a single physical stack frame.
|
|
||||||
type stkframe struct {
|
|
||||||
// fn is the function being run in this frame. If there is
|
|
||||||
// inlining, this is the outermost function.
|
|
||||||
fn funcInfo
|
|
||||||
|
|
||||||
// pc is the program counter within fn.
|
|
||||||
//
|
|
||||||
// The meaning of this is subtle:
|
|
||||||
//
|
|
||||||
// - Typically, this frame performed a regular function call
|
|
||||||
// and this is the return PC (just after the CALL
|
|
||||||
// instruction). In this case, pc-1 reflects the CALL
|
|
||||||
// instruction itself and is the correct source of symbolic
|
|
||||||
// information.
|
|
||||||
//
|
|
||||||
// - If this frame "called" sigpanic, then pc is the
|
|
||||||
// instruction that panicked, and pc is the correct address
|
|
||||||
// to use for symbolic information.
|
|
||||||
//
|
|
||||||
// - If this is the innermost frame, then PC is where
|
|
||||||
// execution will continue, but it may not be the
|
|
||||||
// instruction following a CALL. This may be from
|
|
||||||
// cooperative preemption, in which case this is the
|
|
||||||
// instruction after the call to morestack. Or this may be
|
|
||||||
// from a signal or an un-started goroutine, in which case
|
|
||||||
// PC could be any instruction, including the first
|
|
||||||
// instruction in a function. Conventionally, we use pc-1
|
|
||||||
// for symbolic information, unless pc == fn.entry(), in
|
|
||||||
// which case we use pc.
|
|
||||||
pc uintptr
|
|
||||||
|
|
||||||
// continpc is the PC where execution will continue in fn, or
|
|
||||||
// 0 if execution will not continue in this frame.
|
|
||||||
//
|
|
||||||
// This is usually the same as pc, unless this frame "called"
|
|
||||||
// sigpanic, in which case it's either the address of
|
|
||||||
// deferreturn or 0 if this frame will never execute again.
|
|
||||||
//
|
|
||||||
// This is the PC to use to look up GC liveness for this frame.
|
|
||||||
continpc uintptr
|
|
||||||
|
|
||||||
lr uintptr // program counter at caller aka link register
|
|
||||||
sp uintptr // stack pointer at pc
|
|
||||||
fp uintptr // stack pointer at caller aka frame pointer
|
|
||||||
varp uintptr // top of local variables
|
|
||||||
argp uintptr // pointer to function arguments
|
|
||||||
}
|
|
||||||
|
|
||||||
// ancestorInfo records details of where a goroutine was started.
|
// ancestorInfo records details of where a goroutine was started.
|
||||||
type ancestorInfo struct {
|
type ancestorInfo struct {
|
||||||
pcs []uintptr // pcs from the stack of this goroutine
|
pcs []uintptr // pcs from the stack of this goroutine
|
||||||
|
@ -1247,142 +1247,6 @@ func freeStackSpans() {
|
|||||||
unlock(&stackLarge.lock)
|
unlock(&stackLarge.lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStackMap returns the locals and arguments live pointer maps, and
|
|
||||||
// stack object list for frame.
|
|
||||||
func (frame *stkframe) getStackMap(cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) {
|
|
||||||
targetpc := frame.continpc
|
|
||||||
if targetpc == 0 {
|
|
||||||
// Frame is dead. Return empty bitvectors.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f := frame.fn
|
|
||||||
pcdata := int32(-1)
|
|
||||||
if targetpc != f.entry() {
|
|
||||||
// Back up to the CALL. If we're at the function entry
|
|
||||||
// point, we want to use the entry map (-1), even if
|
|
||||||
// the first instruction of the function changes the
|
|
||||||
// stack map.
|
|
||||||
targetpc--
|
|
||||||
pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache)
|
|
||||||
}
|
|
||||||
if pcdata == -1 {
|
|
||||||
// We do not have a valid pcdata value but there might be a
|
|
||||||
// stackmap for this function. It is likely that we are looking
|
|
||||||
// at the function prologue, assume so and hope for the best.
|
|
||||||
pcdata = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local variables.
|
|
||||||
size := frame.varp - frame.sp
|
|
||||||
var minsize uintptr
|
|
||||||
switch goarch.ArchFamily {
|
|
||||||
case goarch.ARM64:
|
|
||||||
minsize = sys.StackAlign
|
|
||||||
default:
|
|
||||||
minsize = sys.MinFrameSize
|
|
||||||
}
|
|
||||||
if size > minsize {
|
|
||||||
stackid := pcdata
|
|
||||||
stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
|
|
||||||
if stkmap == nil || stkmap.n <= 0 {
|
|
||||||
print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
|
|
||||||
throw("missing stackmap")
|
|
||||||
}
|
|
||||||
// If nbit == 0, there's no work to do.
|
|
||||||
if stkmap.nbit > 0 {
|
|
||||||
if stackid < 0 || stackid >= stkmap.n {
|
|
||||||
// don't know where we are
|
|
||||||
print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
|
|
||||||
throw("bad symbol table")
|
|
||||||
}
|
|
||||||
locals = stackmapdata(stkmap, stackid)
|
|
||||||
if stackDebug >= 3 && debug {
|
|
||||||
print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n")
|
|
||||||
}
|
|
||||||
} else if stackDebug >= 3 && debug {
|
|
||||||
print(" no locals to adjust\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arguments. First fetch frame size and special-case argument maps.
|
|
||||||
var isReflect bool
|
|
||||||
args, isReflect = frame.argMapInternal()
|
|
||||||
if args.n > 0 && args.bytedata == nil {
|
|
||||||
// Non-empty argument frame, but not a special map.
|
|
||||||
// Fetch the argument map at pcdata.
|
|
||||||
stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
|
|
||||||
if stackmap == nil || stackmap.n <= 0 {
|
|
||||||
print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(args.n*goarch.PtrSize), "\n")
|
|
||||||
throw("missing stackmap")
|
|
||||||
}
|
|
||||||
if pcdata < 0 || pcdata >= stackmap.n {
|
|
||||||
// don't know where we are
|
|
||||||
print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
|
|
||||||
throw("bad symbol table")
|
|
||||||
}
|
|
||||||
if stackmap.nbit == 0 {
|
|
||||||
args.n = 0
|
|
||||||
} else {
|
|
||||||
args = stackmapdata(stackmap, pcdata)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stack objects.
|
|
||||||
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
|
|
||||||
unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect {
|
|
||||||
// For reflect.makeFuncStub and reflect.methodValueCall,
|
|
||||||
// we need to fake the stack object record.
|
|
||||||
// These frames contain an internal/abi.RegArgs at a hard-coded offset.
|
|
||||||
// This offset matches the assembly code on amd64 and arm64.
|
|
||||||
objs = methodValueCallFrameObjs[:]
|
|
||||||
} else {
|
|
||||||
p := funcdata(f, _FUNCDATA_StackObjects)
|
|
||||||
if p != nil {
|
|
||||||
n := *(*uintptr)(p)
|
|
||||||
p = add(p, goarch.PtrSize)
|
|
||||||
r0 := (*stackObjectRecord)(noescape(p))
|
|
||||||
objs = unsafe.Slice(r0, int(n))
|
|
||||||
// Note: the noescape above is needed to keep
|
|
||||||
// getStackMap from "leaking param content:
|
|
||||||
// frame". That leak propagates up to getgcmask, then
|
|
||||||
// GCMask, then verifyGCInfo, which converts the stack
|
|
||||||
// gcinfo tests into heap gcinfo tests :(
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit
|
|
||||||
|
|
||||||
func stkobjinit() {
|
|
||||||
var abiRegArgsEface any = 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.
|
// A stackObjectRecord is generated by the compiler for each stack object in a stack frame.
|
||||||
// This record must match the generator code in cmd/compile/internal/liveness/plive.go:emitStackObjects.
|
// This record must match the generator code in cmd/compile/internal/liveness/plive.go:emitStackObjects.
|
||||||
type stackObjectRecord struct {
|
type stackObjectRecord struct {
|
||||||
|
288
src/runtime/stkframe.go
Normal file
288
src/runtime/stkframe.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/abi"
|
||||||
|
"internal/goarch"
|
||||||
|
"runtime/internal/sys"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A stkframe holds information about a single physical stack frame.
|
||||||
|
type stkframe struct {
|
||||||
|
// fn is the function being run in this frame. If there is
|
||||||
|
// inlining, this is the outermost function.
|
||||||
|
fn funcInfo
|
||||||
|
|
||||||
|
// pc is the program counter within fn.
|
||||||
|
//
|
||||||
|
// The meaning of this is subtle:
|
||||||
|
//
|
||||||
|
// - Typically, this frame performed a regular function call
|
||||||
|
// and this is the return PC (just after the CALL
|
||||||
|
// instruction). In this case, pc-1 reflects the CALL
|
||||||
|
// instruction itself and is the correct source of symbolic
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// - If this frame "called" sigpanic, then pc is the
|
||||||
|
// instruction that panicked, and pc is the correct address
|
||||||
|
// to use for symbolic information.
|
||||||
|
//
|
||||||
|
// - If this is the innermost frame, then PC is where
|
||||||
|
// execution will continue, but it may not be the
|
||||||
|
// instruction following a CALL. This may be from
|
||||||
|
// cooperative preemption, in which case this is the
|
||||||
|
// instruction after the call to morestack. Or this may be
|
||||||
|
// from a signal or an un-started goroutine, in which case
|
||||||
|
// PC could be any instruction, including the first
|
||||||
|
// instruction in a function. Conventionally, we use pc-1
|
||||||
|
// for symbolic information, unless pc == fn.entry(), in
|
||||||
|
// which case we use pc.
|
||||||
|
pc uintptr
|
||||||
|
|
||||||
|
// continpc is the PC where execution will continue in fn, or
|
||||||
|
// 0 if execution will not continue in this frame.
|
||||||
|
//
|
||||||
|
// This is usually the same as pc, unless this frame "called"
|
||||||
|
// sigpanic, in which case it's either the address of
|
||||||
|
// deferreturn or 0 if this frame will never execute again.
|
||||||
|
//
|
||||||
|
// This is the PC to use to look up GC liveness for this frame.
|
||||||
|
continpc uintptr
|
||||||
|
|
||||||
|
lr uintptr // program counter at caller aka link register
|
||||||
|
sp uintptr // stack pointer at pc
|
||||||
|
fp uintptr // stack pointer at caller aka frame pointer
|
||||||
|
varp uintptr // top of local variables
|
||||||
|
argp uintptr // pointer to function arguments
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflectMethodValue is a partial duplicate of reflect.makeFuncImpl
|
||||||
|
// and reflect.methodValue.
|
||||||
|
type reflectMethodValue struct {
|
||||||
|
fn uintptr
|
||||||
|
stack *bitvector // ptrmap for both args and results
|
||||||
|
argLen uintptr // just args
|
||||||
|
}
|
||||||
|
|
||||||
|
// argBytes returns the argument frame size for a call to frame.fn.
|
||||||
|
func (frame *stkframe) argBytes() uintptr {
|
||||||
|
if frame.fn.args != _ArgsSizeUnknown {
|
||||||
|
return uintptr(frame.fn.args)
|
||||||
|
}
|
||||||
|
// This is an uncommon and complicated case. Fall back to fully
|
||||||
|
// fetching the argument map to compute its size.
|
||||||
|
argMap, _ := frame.argMapInternal()
|
||||||
|
return uintptr(argMap.n) * goarch.PtrSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// argMapInternal is used internally by stkframe to fetch special
|
||||||
|
// argument maps.
|
||||||
|
//
|
||||||
|
// argMap.n is always populated with the size of the argument map.
|
||||||
|
//
|
||||||
|
// argMap.bytedata is only populated for dynamic argument maps (used
|
||||||
|
// by reflect). If the caller requires the argument map, it should use
|
||||||
|
// this if non-nil, and otherwise fetch the argument map using the
|
||||||
|
// current PC.
|
||||||
|
//
|
||||||
|
// hasReflectStackObj indicates that this frame also has a reflect
|
||||||
|
// function stack object, which the caller must synthesize.
|
||||||
|
func (frame *stkframe) argMapInternal() (argMap bitvector, hasReflectStackObj bool) {
|
||||||
|
f := frame.fn
|
||||||
|
argMap.n = f.args / goarch.PtrSize
|
||||||
|
if f.args == _ArgsSizeUnknown {
|
||||||
|
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
|
||||||
|
switch funcname(f) {
|
||||||
|
case "reflect.makeFuncStub", "reflect.methodValueCall":
|
||||||
|
// These take a *reflect.methodValue as their
|
||||||
|
// context register and immediately save it to 0(SP).
|
||||||
|
// Get the methodValue from 0(SP).
|
||||||
|
arg0 := frame.sp + sys.MinFrameSize
|
||||||
|
|
||||||
|
minSP := frame.fp
|
||||||
|
if !usesLR {
|
||||||
|
// The CALL itself pushes a word.
|
||||||
|
// Undo that adjustment.
|
||||||
|
minSP -= goarch.PtrSize
|
||||||
|
}
|
||||||
|
if arg0 >= minSP {
|
||||||
|
// The function hasn't started yet.
|
||||||
|
// This only happens if f was the
|
||||||
|
// start function of a new goroutine
|
||||||
|
// that hasn't run yet *and* f takes
|
||||||
|
// no arguments and has no results
|
||||||
|
// (otherwise it will get wrapped in a
|
||||||
|
// closure). In this case, we can't
|
||||||
|
// reach into its locals because it
|
||||||
|
// doesn't have locals yet, but we
|
||||||
|
// also know its argument map is
|
||||||
|
// empty.
|
||||||
|
if frame.pc != f.entry() {
|
||||||
|
print("runtime: confused by ", funcname(f), ": no frame (sp=", hex(frame.sp), " fp=", hex(frame.fp), ") at entry+", hex(frame.pc-f.entry()), "\n")
|
||||||
|
throw("reflect mismatch")
|
||||||
|
}
|
||||||
|
return bitvector{}, false // No locals, so also no stack objects
|
||||||
|
}
|
||||||
|
hasReflectStackObj = true
|
||||||
|
mv := *(**reflectMethodValue)(unsafe.Pointer(arg0))
|
||||||
|
// Figure out whether the return values are valid.
|
||||||
|
// Reflect will update this value after it copies
|
||||||
|
// in the return values.
|
||||||
|
retValid := *(*bool)(unsafe.Pointer(arg0 + 4*goarch.PtrSize))
|
||||||
|
if mv.fn != f.entry() {
|
||||||
|
print("runtime: confused by ", funcname(f), "\n")
|
||||||
|
throw("reflect mismatch")
|
||||||
|
}
|
||||||
|
argMap = *mv.stack
|
||||||
|
if !retValid {
|
||||||
|
// argMap.n includes the results, but
|
||||||
|
// those aren't valid, so drop them.
|
||||||
|
n := int32((uintptr(mv.argLen) &^ (goarch.PtrSize - 1)) / goarch.PtrSize)
|
||||||
|
if n < argMap.n {
|
||||||
|
argMap.n = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStackMap returns the locals and arguments live pointer maps, and
|
||||||
|
// stack object list for frame.
|
||||||
|
func (frame *stkframe) getStackMap(cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) {
|
||||||
|
targetpc := frame.continpc
|
||||||
|
if targetpc == 0 {
|
||||||
|
// Frame is dead. Return empty bitvectors.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f := frame.fn
|
||||||
|
pcdata := int32(-1)
|
||||||
|
if targetpc != f.entry() {
|
||||||
|
// Back up to the CALL. If we're at the function entry
|
||||||
|
// point, we want to use the entry map (-1), even if
|
||||||
|
// the first instruction of the function changes the
|
||||||
|
// stack map.
|
||||||
|
targetpc--
|
||||||
|
pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache)
|
||||||
|
}
|
||||||
|
if pcdata == -1 {
|
||||||
|
// We do not have a valid pcdata value but there might be a
|
||||||
|
// stackmap for this function. It is likely that we are looking
|
||||||
|
// at the function prologue, assume so and hope for the best.
|
||||||
|
pcdata = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local variables.
|
||||||
|
size := frame.varp - frame.sp
|
||||||
|
var minsize uintptr
|
||||||
|
switch goarch.ArchFamily {
|
||||||
|
case goarch.ARM64:
|
||||||
|
minsize = sys.StackAlign
|
||||||
|
default:
|
||||||
|
minsize = sys.MinFrameSize
|
||||||
|
}
|
||||||
|
if size > minsize {
|
||||||
|
stackid := pcdata
|
||||||
|
stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
|
||||||
|
if stkmap == nil || stkmap.n <= 0 {
|
||||||
|
print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
|
||||||
|
throw("missing stackmap")
|
||||||
|
}
|
||||||
|
// If nbit == 0, there's no work to do.
|
||||||
|
if stkmap.nbit > 0 {
|
||||||
|
if stackid < 0 || stackid >= stkmap.n {
|
||||||
|
// don't know where we are
|
||||||
|
print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
|
||||||
|
throw("bad symbol table")
|
||||||
|
}
|
||||||
|
locals = stackmapdata(stkmap, stackid)
|
||||||
|
if stackDebug >= 3 && debug {
|
||||||
|
print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n")
|
||||||
|
}
|
||||||
|
} else if stackDebug >= 3 && debug {
|
||||||
|
print(" no locals to adjust\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arguments. First fetch frame size and special-case argument maps.
|
||||||
|
var isReflect bool
|
||||||
|
args, isReflect = frame.argMapInternal()
|
||||||
|
if args.n > 0 && args.bytedata == nil {
|
||||||
|
// Non-empty argument frame, but not a special map.
|
||||||
|
// Fetch the argument map at pcdata.
|
||||||
|
stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
|
||||||
|
if stackmap == nil || stackmap.n <= 0 {
|
||||||
|
print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(args.n*goarch.PtrSize), "\n")
|
||||||
|
throw("missing stackmap")
|
||||||
|
}
|
||||||
|
if pcdata < 0 || pcdata >= stackmap.n {
|
||||||
|
// don't know where we are
|
||||||
|
print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
|
||||||
|
throw("bad symbol table")
|
||||||
|
}
|
||||||
|
if stackmap.nbit == 0 {
|
||||||
|
args.n = 0
|
||||||
|
} else {
|
||||||
|
args = stackmapdata(stackmap, pcdata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stack objects.
|
||||||
|
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
|
||||||
|
unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect {
|
||||||
|
// For reflect.makeFuncStub and reflect.methodValueCall,
|
||||||
|
// we need to fake the stack object record.
|
||||||
|
// These frames contain an internal/abi.RegArgs at a hard-coded offset.
|
||||||
|
// This offset matches the assembly code on amd64 and arm64.
|
||||||
|
objs = methodValueCallFrameObjs[:]
|
||||||
|
} else {
|
||||||
|
p := funcdata(f, _FUNCDATA_StackObjects)
|
||||||
|
if p != nil {
|
||||||
|
n := *(*uintptr)(p)
|
||||||
|
p = add(p, goarch.PtrSize)
|
||||||
|
r0 := (*stackObjectRecord)(noescape(p))
|
||||||
|
objs = unsafe.Slice(r0, int(n))
|
||||||
|
// Note: the noescape above is needed to keep
|
||||||
|
// getStackMap from "leaking param content:
|
||||||
|
// frame". That leak propagates up to getgcmask, then
|
||||||
|
// GCMask, then verifyGCInfo, which converts the stack
|
||||||
|
// gcinfo tests into heap gcinfo tests :(
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit
|
||||||
|
|
||||||
|
func stkobjinit() {
|
||||||
|
var abiRegArgsEface any = 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),
|
||||||
|
}
|
||||||
|
}
|
@ -648,97 +648,6 @@ printloop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reflectMethodValue is a partial duplicate of reflect.makeFuncImpl
|
|
||||||
// and reflect.methodValue.
|
|
||||||
type reflectMethodValue struct {
|
|
||||||
fn uintptr
|
|
||||||
stack *bitvector // ptrmap for both args and results
|
|
||||||
argLen uintptr // just args
|
|
||||||
}
|
|
||||||
|
|
||||||
// argBytes returns the argument frame size for a call to frame.fn.
|
|
||||||
func (frame *stkframe) argBytes() uintptr {
|
|
||||||
if frame.fn.args != _ArgsSizeUnknown {
|
|
||||||
return uintptr(frame.fn.args)
|
|
||||||
}
|
|
||||||
// This is an uncommon and complicated case. Fall back to fully
|
|
||||||
// fetching the argument map to compute its size.
|
|
||||||
argMap, _ := frame.argMapInternal()
|
|
||||||
return uintptr(argMap.n) * goarch.PtrSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// argMapInternal is used internally by stkframe to fetch special
|
|
||||||
// argument maps.
|
|
||||||
//
|
|
||||||
// argMap.n is always populated with the size of the argument map.
|
|
||||||
//
|
|
||||||
// argMap.bytedata is only populated for dynamic argument maps (used
|
|
||||||
// by reflect). If the caller requires the argument map, it should use
|
|
||||||
// this if non-nil, and otherwise fetch the argument map using the
|
|
||||||
// current PC.
|
|
||||||
//
|
|
||||||
// hasReflectStackObj indicates that this frame also has a reflect
|
|
||||||
// function stack object, which the caller must synthesize.
|
|
||||||
func (frame *stkframe) argMapInternal() (argMap bitvector, hasReflectStackObj bool) {
|
|
||||||
f := frame.fn
|
|
||||||
argMap.n = f.args / goarch.PtrSize
|
|
||||||
if f.args == _ArgsSizeUnknown {
|
|
||||||
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
|
|
||||||
switch funcname(f) {
|
|
||||||
case "reflect.makeFuncStub", "reflect.methodValueCall":
|
|
||||||
// These take a *reflect.methodValue as their
|
|
||||||
// context register and immediately save it to 0(SP).
|
|
||||||
// Get the methodValue from 0(SP).
|
|
||||||
arg0 := frame.sp + sys.MinFrameSize
|
|
||||||
|
|
||||||
minSP := frame.fp
|
|
||||||
if !usesLR {
|
|
||||||
// The CALL itself pushes a word.
|
|
||||||
// Undo that adjustment.
|
|
||||||
minSP -= goarch.PtrSize
|
|
||||||
}
|
|
||||||
if arg0 >= minSP {
|
|
||||||
// The function hasn't started yet.
|
|
||||||
// This only happens if f was the
|
|
||||||
// start function of a new goroutine
|
|
||||||
// that hasn't run yet *and* f takes
|
|
||||||
// no arguments and has no results
|
|
||||||
// (otherwise it will get wrapped in a
|
|
||||||
// closure). In this case, we can't
|
|
||||||
// reach into its locals because it
|
|
||||||
// doesn't have locals yet, but we
|
|
||||||
// also know its argument map is
|
|
||||||
// empty.
|
|
||||||
if frame.pc != f.entry() {
|
|
||||||
print("runtime: confused by ", funcname(f), ": no frame (sp=", hex(frame.sp), " fp=", hex(frame.fp), ") at entry+", hex(frame.pc-f.entry()), "\n")
|
|
||||||
throw("reflect mismatch")
|
|
||||||
}
|
|
||||||
return bitvector{}, false // No locals, so also no stack objects
|
|
||||||
}
|
|
||||||
hasReflectStackObj = true
|
|
||||||
mv := *(**reflectMethodValue)(unsafe.Pointer(arg0))
|
|
||||||
// Figure out whether the return values are valid.
|
|
||||||
// Reflect will update this value after it copies
|
|
||||||
// in the return values.
|
|
||||||
retValid := *(*bool)(unsafe.Pointer(arg0 + 4*goarch.PtrSize))
|
|
||||||
if mv.fn != f.entry() {
|
|
||||||
print("runtime: confused by ", funcname(f), "\n")
|
|
||||||
throw("reflect mismatch")
|
|
||||||
}
|
|
||||||
argMap = *mv.stack
|
|
||||||
if !retValid {
|
|
||||||
// argMap.n includes the results, but
|
|
||||||
// those aren't valid, so drop them.
|
|
||||||
n := int32((uintptr(mv.argLen) &^ (goarch.PtrSize - 1)) / goarch.PtrSize)
|
|
||||||
if n < argMap.n {
|
|
||||||
argMap.n = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// tracebackCgoContext handles tracing back a cgo context value, from
|
// tracebackCgoContext handles tracing back a cgo context value, from
|
||||||
// the context argument to setCgoTraceback, for the gentraceback
|
// the context argument to setCgoTraceback, for the gentraceback
|
||||||
// function. It returns the new value of n.
|
// function. It returns the new value of n.
|
||||||
|
Loading…
Reference in New Issue
Block a user