github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/stkframe.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 import ( 8 "internal/abi" 9 "internal/goarch" 10 "runtime/internal/sys" 11 "unsafe" 12 ) 13 14 // A stkframe holds information about a single physical stack frame. 15 type stkframe struct { 16 // fn is the function being run in this frame. If there is 17 // inlining, this is the outermost function. 18 fn funcInfo 19 20 // pc is the program counter within fn. 21 // 22 // The meaning of this is subtle: 23 // 24 // - Typically, this frame performed a regular function call 25 // and this is the return PC (just after the CALL 26 // instruction). In this case, pc-1 reflects the CALL 27 // instruction itself and is the correct source of symbolic 28 // information. 29 // 30 // - If this frame "called" sigpanic, then pc is the 31 // instruction that panicked, and pc is the correct address 32 // to use for symbolic information. 33 // 34 // - If this is the innermost frame, then PC is where 35 // execution will continue, but it may not be the 36 // instruction following a CALL. This may be from 37 // cooperative preemption, in which case this is the 38 // instruction after the call to morestack. Or this may be 39 // from a signal or an un-started goroutine, in which case 40 // PC could be any instruction, including the first 41 // instruction in a function. Conventionally, we use pc-1 42 // for symbolic information, unless pc == fn.entry(), in 43 // which case we use pc. 44 pc uintptr 45 46 // continpc is the PC where execution will continue in fn, or 47 // 0 if execution will not continue in this frame. 48 // 49 // This is usually the same as pc, unless this frame "called" 50 // sigpanic, in which case it's either the address of 51 // deferreturn or 0 if this frame will never execute again. 52 // 53 // This is the PC to use to look up GC liveness for this frame. 54 continpc uintptr 55 56 lr uintptr // program counter at caller aka link register 57 sp uintptr // stack pointer at pc 58 fp uintptr // stack pointer at caller aka frame pointer 59 varp uintptr // top of local variables 60 argp uintptr // pointer to function arguments 61 } 62 63 // reflectMethodValue is a partial duplicate of reflect.makeFuncImpl 64 // and reflect.methodValue. 65 type reflectMethodValue struct { 66 fn uintptr 67 stack *bitvector // ptrmap for both args and results 68 argLen uintptr // just args 69 } 70 71 // argBytes returns the argument frame size for a call to frame.fn. 72 func (frame *stkframe) argBytes() uintptr { 73 if frame.fn.args != abi.ArgsSizeUnknown { 74 return uintptr(frame.fn.args) 75 } 76 // This is an uncommon and complicated case. Fall back to fully 77 // fetching the argument map to compute its size. 78 argMap, _ := frame.argMapInternal() 79 return uintptr(argMap.n) * goarch.PtrSize 80 } 81 82 // argMapInternal is used internally by stkframe to fetch special 83 // argument maps. 84 // 85 // argMap.n is always populated with the size of the argument map. 86 // 87 // argMap.bytedata is only populated for dynamic argument maps (used 88 // by reflect). If the caller requires the argument map, it should use 89 // this if non-nil, and otherwise fetch the argument map using the 90 // current PC. 91 // 92 // hasReflectStackObj indicates that this frame also has a reflect 93 // function stack object, which the caller must synthesize. 94 func (frame *stkframe) argMapInternal() (argMap bitvector, hasReflectStackObj bool) { 95 f := frame.fn 96 if f.args != abi.ArgsSizeUnknown { 97 argMap.n = f.args / goarch.PtrSize 98 return 99 } 100 // Extract argument bitmaps for reflect stubs from the calls they made to reflect. 101 switch funcname(f) { 102 case "reflect.makeFuncStub", "reflect.methodValueCall": 103 // These take a *reflect.methodValue as their 104 // context register and immediately save it to 0(SP). 105 // Get the methodValue from 0(SP). 106 arg0 := frame.sp + sys.MinFrameSize 107 108 minSP := frame.fp 109 if !usesLR { 110 // The CALL itself pushes a word. 111 // Undo that adjustment. 112 minSP -= goarch.PtrSize 113 } 114 if arg0 >= minSP { 115 // The function hasn't started yet. 116 // This only happens if f was the 117 // start function of a new goroutine 118 // that hasn't run yet *and* f takes 119 // no arguments and has no results 120 // (otherwise it will get wrapped in a 121 // closure). In this case, we can't 122 // reach into its locals because it 123 // doesn't have locals yet, but we 124 // also know its argument map is 125 // empty. 126 if frame.pc != f.entry() { 127 print("runtime: confused by ", funcname(f), ": no frame (sp=", hex(frame.sp), " fp=", hex(frame.fp), ") at entry+", hex(frame.pc-f.entry()), "\n") 128 throw("reflect mismatch") 129 } 130 return bitvector{}, false // No locals, so also no stack objects 131 } 132 hasReflectStackObj = true 133 mv := *(**reflectMethodValue)(unsafe.Pointer(arg0)) 134 // Figure out whether the return values are valid. 135 // Reflect will update this value after it copies 136 // in the return values. 137 retValid := *(*bool)(unsafe.Pointer(arg0 + 4*goarch.PtrSize)) 138 if mv.fn != f.entry() { 139 print("runtime: confused by ", funcname(f), "\n") 140 throw("reflect mismatch") 141 } 142 argMap = *mv.stack 143 if !retValid { 144 // argMap.n includes the results, but 145 // those aren't valid, so drop them. 146 n := int32((uintptr(mv.argLen) &^ (goarch.PtrSize - 1)) / goarch.PtrSize) 147 if n < argMap.n { 148 argMap.n = n 149 } 150 } 151 } 152 return 153 } 154 155 // getStackMap returns the locals and arguments live pointer maps, and 156 // stack object list for frame. 157 func (frame *stkframe) getStackMap(cache *pcvalueCache, debug bool) (locals, args bitvector, objs []stackObjectRecord) { 158 targetpc := frame.continpc 159 if targetpc == 0 { 160 // Frame is dead. Return empty bitvectors. 161 return 162 } 163 164 f := frame.fn 165 pcdata := int32(-1) 166 if targetpc != f.entry() { 167 // Back up to the CALL. If we're at the function entry 168 // point, we want to use the entry map (-1), even if 169 // the first instruction of the function changes the 170 // stack map. 171 targetpc-- 172 pcdata = pcdatavalue(f, abi.PCDATA_StackMapIndex, targetpc, cache) 173 } 174 if pcdata == -1 { 175 // We do not have a valid pcdata value but there might be a 176 // stackmap for this function. It is likely that we are looking 177 // at the function prologue, assume so and hope for the best. 178 pcdata = 0 179 } 180 181 // Local variables. 182 size := frame.varp - frame.sp 183 var minsize uintptr 184 switch goarch.ArchFamily { 185 case goarch.ARM64: 186 minsize = sys.StackAlign 187 default: 188 minsize = sys.MinFrameSize 189 } 190 if size > minsize { 191 stackid := pcdata 192 stkmap := (*stackmap)(funcdata(f, abi.FUNCDATA_LocalsPointerMaps)) 193 if stkmap == nil || stkmap.n <= 0 { 194 print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") 195 throw("missing stackmap") 196 } 197 // If nbit == 0, there's no work to do. 198 if stkmap.nbit > 0 { 199 if stackid < 0 || stackid >= stkmap.n { 200 // don't know where we are 201 print("runtime: pcdata is ", stackid, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") 202 throw("bad symbol table") 203 } 204 locals = stackmapdata(stkmap, stackid) 205 if stackDebug >= 3 && debug { 206 print(" locals ", stackid, "/", stkmap.n, " ", locals.n, " words ", locals.bytedata, "\n") 207 } 208 } else if stackDebug >= 3 && debug { 209 print(" no locals to adjust\n") 210 } 211 } 212 213 // Arguments. First fetch frame size and special-case argument maps. 214 var isReflect bool 215 args, isReflect = frame.argMapInternal() 216 if args.n > 0 && args.bytedata == nil { 217 // Non-empty argument frame, but not a special map. 218 // Fetch the argument map at pcdata. 219 stackmap := (*stackmap)(funcdata(f, abi.FUNCDATA_ArgsPointerMaps)) 220 if stackmap == nil || stackmap.n <= 0 { 221 print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(args.n*goarch.PtrSize), "\n") 222 throw("missing stackmap") 223 } 224 if pcdata < 0 || pcdata >= stackmap.n { 225 // don't know where we are 226 print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") 227 throw("bad symbol table") 228 } 229 if stackmap.nbit == 0 { 230 args.n = 0 231 } else { 232 args = stackmapdata(stackmap, pcdata) 233 } 234 } 235 236 // stack objects. 237 if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && 238 unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect { 239 // For reflect.makeFuncStub and reflect.methodValueCall, 240 // we need to fake the stack object record. 241 // These frames contain an internal/abi.RegArgs at a hard-coded offset. 242 // This offset matches the assembly code on amd64 and arm64. 243 objs = methodValueCallFrameObjs[:] 244 } else { 245 p := funcdata(f, abi.FUNCDATA_StackObjects) 246 if p != nil { 247 n := *(*uintptr)(p) 248 p = add(p, goarch.PtrSize) 249 r0 := (*stackObjectRecord)(noescape(p)) 250 objs = unsafe.Slice(r0, int(n)) 251 // Note: the noescape above is needed to keep 252 // getStackMap from "leaking param content: 253 // frame". That leak propagates up to getgcmask, then 254 // GCMask, then verifyGCInfo, which converts the stack 255 // gcinfo tests into heap gcinfo tests :( 256 } 257 } 258 259 return 260 } 261 262 var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit 263 264 func stkobjinit() { 265 var abiRegArgsEface any = abi.RegArgs{} 266 abiRegArgsType := efaceOf(&abiRegArgsEface)._type 267 if abiRegArgsType.Kind_&kindGCProg != 0 { 268 throw("abiRegArgsType needs GC Prog, update methodValueCallFrameObjs") 269 } 270 // Set methodValueCallFrameObjs[0].gcdataoff so that 271 // stackObjectRecord.gcdata() will work correctly with it. 272 ptr := uintptr(unsafe.Pointer(&methodValueCallFrameObjs[0])) 273 var mod *moduledata 274 for datap := &firstmoduledata; datap != nil; datap = datap.next { 275 if datap.gofunc <= ptr && ptr < datap.end { 276 mod = datap 277 break 278 } 279 } 280 if mod == nil { 281 throw("methodValueCallFrameObjs is not in a module") 282 } 283 methodValueCallFrameObjs[0] = stackObjectRecord{ 284 off: -int32(alignUp(abiRegArgsType.Size_, 8)), // It's always the highest address local. 285 size: int32(abiRegArgsType.Size_), 286 _ptrdata: int32(abiRegArgsType.PtrBytes), 287 gcdataoff: uint32(uintptr(unsafe.Pointer(abiRegArgsType.GCData)) - mod.rodata), 288 } 289 }