Source File
stkframe.go
Belonging Package
runtime
// 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 (
)
// 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 ( *stkframe) () uintptr {
if .fn.args != abi.ArgsSizeUnknown {
return uintptr(.fn.args)
}
// This is an uncommon and complicated case. Fall back to fully
// fetching the argument map to compute its size.
, := .argMapInternal()
return uintptr(.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 ( *stkframe) () ( bitvector, bool) {
:= .fn
if .args != abi.ArgsSizeUnknown {
.n = .args / goarch.PtrSize
return
}
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
switch funcname() {
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).
:= .sp + sys.MinFrameSize
:= .fp
if !usesLR {
// The CALL itself pushes a word.
// Undo that adjustment.
-= goarch.PtrSize
}
if >= {
// 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 .pc != .entry() {
print("runtime: confused by ", funcname(), ": no frame (sp=", hex(.sp), " fp=", hex(.fp), ") at entry+", hex(.pc-.entry()), "\n")
throw("reflect mismatch")
}
return bitvector{}, false // No locals, so also no stack objects
}
= true
:= *(**reflectMethodValue)(unsafe.Pointer())
// Figure out whether the return values are valid.
// Reflect will update this value after it copies
// in the return values.
:= *(*bool)(unsafe.Pointer( + 4*goarch.PtrSize))
if .fn != .entry() {
print("runtime: confused by ", funcname(), "\n")
throw("reflect mismatch")
}
= *.stack
if ! {
// argMap.n includes the results, but
// those aren't valid, so drop them.
:= int32((.argLen &^ (goarch.PtrSize - 1)) / goarch.PtrSize)
if < .n {
.n =
}
}
}
return
}
// getStackMap returns the locals and arguments live pointer maps, and
// stack object list for frame.
func ( *stkframe) ( bool) (, bitvector, []stackObjectRecord) {
:= .continpc
if == 0 {
// Frame is dead. Return empty bitvectors.
return
}
:= .fn
:= int32(-1)
if != .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.
--
= pcdatavalue(, abi.PCDATA_StackMapIndex, )
}
if == -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.
= 0
}
// Local variables.
:= .varp - .sp
var uintptr
switch goarch.ArchFamily {
case goarch.ARM64:
= sys.StackAlign
default:
= sys.MinFrameSize
}
if > {
:=
:= (*stackmap)(funcdata(, abi.FUNCDATA_LocalsPointerMaps))
if == nil || .n <= 0 {
print("runtime: frame ", funcname(), " untyped locals ", hex(.varp-), "+", hex(), "\n")
throw("missing stackmap")
}
// If nbit == 0, there's no work to do.
if .nbit > 0 {
if < 0 || >= .n {
// don't know where we are
print("runtime: pcdata is ", , " and ", .n, " locals stack map entries for ", funcname(), " (targetpc=", hex(), ")\n")
throw("bad symbol table")
}
= stackmapdata(, )
if stackDebug >= 3 && {
print(" locals ", , "/", .n, " ", .n, " words ", .bytedata, "\n")
}
} else if stackDebug >= 3 && {
print(" no locals to adjust\n")
}
}
// Arguments. First fetch frame size and special-case argument maps.
var bool
, = .argMapInternal()
if .n > 0 && .bytedata == nil {
// Non-empty argument frame, but not a special map.
// Fetch the argument map at pcdata.
:= (*stackmap)(funcdata(, abi.FUNCDATA_ArgsPointerMaps))
if == nil || .n <= 0 {
print("runtime: frame ", funcname(), " untyped args ", hex(.argp), "+", hex(.n*goarch.PtrSize), "\n")
throw("missing stackmap")
}
if < 0 || >= .n {
// don't know where we are
print("runtime: pcdata is ", , " and ", .n, " args stack map entries for ", funcname(), " (targetpc=", hex(), ")\n")
throw("bad symbol table")
}
if .nbit == 0 {
.n = 0
} else {
= stackmapdata(, )
}
}
// stack objects.
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "loong64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
unsafe.Sizeof(abi.RegArgs{}) > 0 && {
// 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.
= methodValueCallFrameObjs[:]
} else {
:= funcdata(, abi.FUNCDATA_StackObjects)
if != nil {
:= *(*uintptr)()
= add(, goarch.PtrSize)
:= (*stackObjectRecord)(noescape())
= unsafe.Slice(, int())
// 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 () {
var any = abi.RegArgs{}
:= efaceOf(&)._type
// Set methodValueCallFrameObjs[0].gcdataoff so that
// stackObjectRecord.gcdata() will work correctly with it.
:= uintptr(unsafe.Pointer(&methodValueCallFrameObjs[0]))
var *moduledata
for := &firstmoduledata; != nil; = .next {
if .gofunc <= && < .end {
=
break
}
}
if == nil {
throw("methodValueCallFrameObjs is not in a module")
}
methodValueCallFrameObjs[0] = stackObjectRecord{
off: -int32(alignUp(.Size_, 8)), // It's always the highest address local.
size: int32(.Size_),
ptrBytes: int32(.PtrBytes),
gcdataoff: uint32(uintptr(unsafe.Pointer(getGCMask())) - .rodata),
}
}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)