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
5package runtime
6
7import (
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.
15type 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.
65type 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.
72func (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.
94func (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((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.
157func (frame *stkframe) getStackMap(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)
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 == "loong64" || 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
262var methodValueCallFrameObjs [1]stackObjectRecord // initialized in stackobjectinit
263
264func stkobjinit() {
265	var abiRegArgsEface any = abi.RegArgs{}
266	abiRegArgsType := efaceOf(&abiRegArgsEface)._type
267	if abiRegArgsType.Kind_&abi.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}
290