summaryrefslogtreecommitdiffstats
path: root/src/runtime/stkframe.go
blob: 3ecf3a828c27e754a4cb499d3070c162f8636f5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// 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
	if f.args != _ArgsSizeUnknown {
		argMap.n = f.args / goarch.PtrSize
		return
	}
	// 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),
	}
}