// 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 runtimeimport ()// 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 {returnuintptr(.fn.args) }// This is an uncommon and complicated case. Fall back to fully // fetching the argument map to compute its size. , := .argMapInternal()returnuintptr(.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) { := .fnif .args != abi.ArgsSizeUnknown { .n = .args / goarch.PtrSizereturn }// Extract argument bitmaps for reflect stubs from the calls they made to reflect.switchfuncname() {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 := .fpif !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") }returnbitvector{}, 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") } = *.stackif ! {// 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) { := .continpcif == 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 - .spvaruintptrswitchgoarch.ArchFamily {casegoarch.ARM64: = sys.StackAligndefault: = 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 areprint("runtime: pcdata is ", , " and ", .n, " locals stack map entries for ", funcname(), " (targetpc=", hex(), ")\n")throw("bad symbol table") } = stackmapdata(, )ifstackDebug >= 3 && {print(" locals ", , "/", .n, " ", .n, " words ", .bytedata, "\n") } } elseifstackDebug >= 3 && {print(" no locals to adjust\n") } }// Arguments. First fetch frame size and special-case argument maps.varbool , = .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 areprint("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 stackobjectinitfunc stkobjinit() {varany = abi.RegArgs{} := efaceOf(&)._typeif .Kind_&abi.KindGCProg != 0 {throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs") }// Set methodValueCallFrameObjs[0].gcdataoff so that // stackObjectRecord.gcdata() will work correctly with it. := uintptr(unsafe.Pointer(&methodValueCallFrameObjs[0]))var *moduledatafor := &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_),_ptrdata: int32(.PtrBytes),gcdataoff: uint32(uintptr(unsafe.Pointer(.GCData)) - .rodata), }}
The pages are generated with Goldsv0.6.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.