aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/export_debug_arm64_test.go
blob: ee902414afe92afc27eb8ae77f887975d997f884 (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
// 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.

//go:build arm64 && linux

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"unsafe"
)

type sigContext struct {
	savedRegs sigcontext
}

func sigctxtSetContextRegister(ctxt *sigctxt, x uint64) {
	ctxt.regs().regs[26] = x
}

func sigctxtAtTrapInstruction(ctxt *sigctxt) bool {
	return *(*uint32)(unsafe.Pointer(ctxt.sigpc())) == 0xd4200000 // BRK 0
}

func sigctxtStatus(ctxt *sigctxt) uint64 {
	return ctxt.r20()
}

func (h *debugCallHandler) saveSigContext(ctxt *sigctxt) {
	sp := ctxt.sp()
	sp -= 2 * goarch.PtrSize
	ctxt.set_sp(sp)
	*(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.lr() // save the current lr
	ctxt.set_lr(ctxt.pc())                              // set new lr to the current pc
	// Write the argument frame size.
	*(*uintptr)(unsafe.Pointer(uintptr(sp - 16))) = h.argSize
	// Save current registers.
	h.sigCtxt.savedRegs = *ctxt.regs()
}

// case 0
func (h *debugCallHandler) debugCallRun(ctxt *sigctxt) {
	sp := ctxt.sp()
	memmove(unsafe.Pointer(uintptr(sp)+8), h.argp, h.argSize)
	if h.regArgs != nil {
		storeRegArgs(ctxt.regs(), h.regArgs)
	}
	// Push return PC, which should be the signal PC+4, because
	// the signal PC is the PC of the trap instruction itself.
	ctxt.set_lr(ctxt.pc() + 4)
	// Set PC to call and context register.
	ctxt.set_pc(uint64(h.fv.fn))
	sigctxtSetContextRegister(ctxt, uint64(uintptr(unsafe.Pointer(h.fv))))
}

// case 1
func (h *debugCallHandler) debugCallReturn(ctxt *sigctxt) {
	sp := ctxt.sp()
	memmove(h.argp, unsafe.Pointer(uintptr(sp)+8), h.argSize)
	if h.regArgs != nil {
		loadRegArgs(h.regArgs, ctxt.regs())
	}
	// Restore the old lr from *sp
	olr := *(*uint64)(unsafe.Pointer(uintptr(sp)))
	ctxt.set_lr(olr)
	pc := ctxt.pc()
	ctxt.set_pc(pc + 4) // step to next instruction
}

// case 2
func (h *debugCallHandler) debugCallPanicOut(ctxt *sigctxt) {
	sp := ctxt.sp()
	memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)+8), 2*goarch.PtrSize)
	ctxt.set_pc(ctxt.pc() + 4)
}

// case 8
func (h *debugCallHandler) debugCallUnsafe(ctxt *sigctxt) {
	sp := ctxt.sp()
	reason := *(*string)(unsafe.Pointer(uintptr(sp) + 8))
	h.err = plainError(reason)
	ctxt.set_pc(ctxt.pc() + 4)
}

// case 16
func (h *debugCallHandler) restoreSigContext(ctxt *sigctxt) {
	// Restore all registers except for pc and sp
	pc, sp := ctxt.pc(), ctxt.sp()
	*ctxt.regs() = h.sigCtxt.savedRegs
	ctxt.set_pc(pc + 4)
	ctxt.set_sp(sp)
}

// storeRegArgs sets up argument registers in the signal
// context state from an abi.RegArgs.
//
// Both src and dst must be non-nil.
func storeRegArgs(dst *sigcontext, src *abi.RegArgs) {
	for i, r := range src.Ints {
		dst.regs[i] = uint64(r)
	}
	for i, r := range src.Floats {
		*(fpRegAddr(dst, i)) = r
	}
}

func loadRegArgs(dst *abi.RegArgs, src *sigcontext) {
	for i := range dst.Ints {
		dst.Ints[i] = uintptr(src.regs[i])
	}
	for i := range dst.Floats {
		dst.Floats[i] = *(fpRegAddr(src, i))
	}
}

// fpRegAddr returns the address of the ith fp-simd register in sigcontext.
func fpRegAddr(dst *sigcontext, i int) *uint64 {
	/* FP-SIMD registers are saved in sigcontext.__reserved, which is orgnized in
	the following C structs:
	struct fpsimd_context {
		struct _aarch64_ctx head;
		__u32 fpsr;
		__u32 fpcr;
		__uint128_t vregs[32];
	};
	struct _aarch64_ctx {
		__u32 magic;
		__u32 size;
	};
	So the offset of the ith FP_SIMD register is 16+i*128.
	*/
	return (*uint64)(unsafe.Pointer(&dst.__reserved[16+i*128]))
}