Skip to content

Commit 3108366

Browse files
committed
runtime: deduplicate context call injection on Windows
Injecting a call to a thread context is complex enough to warrant a dedicated function so that we don't repeat the same code in multiple places. Note that the unix sigctxt struct also follows the same approach. The behavior is unchanged, but the implementation semantics are now clearer by using goarch.StackAlign instead of a mix of goarch.PtrSize, goarch.StackAlign and hardcoded values. While here, fix #68552. Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64 Change-Id: Ic29cd2bf322b520127fecccafd61577076945758 Reviewed-on: https://go-review.googlesource.com/c/go/+/657815 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 2cc0ea4 commit 3108366

6 files changed

+64
-49
lines changed

src/runtime/defs_windows_386.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
package runtime
66

7+
import (
8+
"internal/goarch"
9+
"unsafe"
10+
)
11+
712
const _CONTEXT_CONTROL = 0x10001
813

914
type floatingsavearea struct {
@@ -59,6 +64,13 @@ func (c *context) set_sp(x uintptr) { c.esp = uint32(x) }
5964
// 386 does not have frame pointer register.
6065
func (c *context) set_fp(x uintptr) {}
6166

67+
func (c *context) pushCall(targetPC, resumePC uintptr) {
68+
sp := c.sp() - goarch.StackAlign
69+
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
70+
c.set_sp(sp)
71+
c.set_ip(targetPC)
72+
}
73+
6274
func prepareContextForSigResume(c *context) {
6375
c.edx = c.esp
6476
c.ecx = c.eip

src/runtime/defs_windows_amd64.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
package runtime
66

7+
import (
8+
"internal/goarch"
9+
"unsafe"
10+
)
11+
712
const _CONTEXT_CONTROL = 0x100001
813

914
type m128a struct {
@@ -71,6 +76,13 @@ func (c *context) set_ip(x uintptr) { c.rip = uint64(x) }
7176
func (c *context) set_sp(x uintptr) { c.rsp = uint64(x) }
7277
func (c *context) set_fp(x uintptr) { c.rbp = uint64(x) }
7378

79+
func (c *context) pushCall(targetPC, resumePC uintptr) {
80+
sp := c.sp() - goarch.StackAlign
81+
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
82+
c.set_sp(sp)
83+
c.set_ip(targetPC)
84+
}
85+
7486
func prepareContextForSigResume(c *context) {
7587
c.r8 = c.rsp
7688
c.r9 = c.rip

src/runtime/defs_windows_arm.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
package runtime
66

7+
import (
8+
"internal/goarch"
9+
"unsafe"
10+
)
11+
712
// NOTE(rsc): _CONTEXT_CONTROL is actually 0x200001 and should include PC, SP, and LR.
813
// However, empirically, LR doesn't come along on Windows 10
914
// unless you also set _CONTEXT_INTEGER (0x200002).
@@ -61,6 +66,18 @@ func (c *context) set_lr(x uintptr) { c.lrr = uint32(x) }
6166
// arm does not have frame pointer register.
6267
func (c *context) set_fp(x uintptr) {}
6368

69+
func (c *context) pushCall(targetPC, resumePC uintptr) {
70+
// Push LR. The injected call is responsible
71+
// for restoring LR. gentraceback is aware of
72+
// this extra slot. See sigctxt.pushCall in
73+
// signal_arm.go.
74+
sp := c.sp() - goarch.StackAlign
75+
c.set_sp(sp)
76+
*(*uint32)(unsafe.Pointer(sp)) = uint32(c.lr())
77+
c.set_lr(resumePC)
78+
c.set_ip(targetPC)
79+
}
80+
6481
func prepareContextForSigResume(c *context) {
6582
c.r0 = c.spr
6683
c.r1 = c.pc

src/runtime/defs_windows_arm64.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
package runtime
66

7+
import (
8+
"internal/goarch"
9+
"unsafe"
10+
)
11+
712
// NOTE(rsc): _CONTEXT_CONTROL is actually 0x400001 and should include PC, SP, and LR.
813
// However, empirically, LR doesn't come along on Windows 10
914
// unless you also set _CONTEXT_INTEGER (0x400002).
@@ -42,6 +47,18 @@ func (c *context) set_sp(x uintptr) { c.xsp = uint64(x) }
4247
func (c *context) set_lr(x uintptr) { c.x[30] = uint64(x) }
4348
func (c *context) set_fp(x uintptr) { c.x[29] = uint64(x) }
4449

50+
func (c *context) pushCall(targetPC, resumePC uintptr) {
51+
// Push LR. The injected call is responsible
52+
// for restoring LR. gentraceback is aware of
53+
// this extra slot. See sigctxt.pushCall in
54+
// signal_arm64.go.
55+
sp := c.sp() - goarch.StackAlign
56+
c.set_sp(sp)
57+
*(*uint64)(unsafe.Pointer(sp)) = uint64(c.lr())
58+
c.set_lr(resumePC)
59+
c.set_ip(targetPC)
60+
}
61+
4562
func prepareContextForSigResume(c *context) {
4663
c.x[0] = c.xsp
4764
c.x[1] = c.pc

src/runtime/os_windows.go

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runtime
66

77
import (
88
"internal/abi"
9-
"internal/goarch"
109
"internal/runtime/atomic"
1110
"internal/runtime/sys"
1211
"unsafe"
@@ -1302,44 +1301,10 @@ func preemptM(mp *m) {
13021301
// Does it want a preemption and is it safe to preempt?
13031302
gp := gFromSP(mp, c.sp())
13041303
if gp != nil && wantAsyncPreempt(gp) {
1305-
if ok, newpc := isAsyncSafePoint(gp, c.ip(), c.sp(), c.lr()); ok {
1304+
if ok, resumePC := isAsyncSafePoint(gp, c.ip(), c.sp(), c.lr()); ok {
13061305
// Inject call to asyncPreempt
13071306
targetPC := abi.FuncPCABI0(asyncPreempt)
1308-
switch GOARCH {
1309-
default:
1310-
throw("unsupported architecture")
1311-
case "386", "amd64":
1312-
// Make it look like the thread called targetPC.
1313-
sp := c.sp()
1314-
sp -= goarch.PtrSize
1315-
*(*uintptr)(unsafe.Pointer(sp)) = newpc
1316-
c.set_sp(sp)
1317-
c.set_ip(targetPC)
1318-
1319-
case "arm":
1320-
// Push LR. The injected call is responsible
1321-
// for restoring LR. gentraceback is aware of
1322-
// this extra slot. See sigctxt.pushCall in
1323-
// signal_arm.go, which is similar except we
1324-
// subtract 1 from IP here.
1325-
sp := c.sp()
1326-
sp -= goarch.PtrSize
1327-
c.set_sp(sp)
1328-
*(*uint32)(unsafe.Pointer(sp)) = uint32(c.lr())
1329-
c.set_lr(newpc - 1)
1330-
c.set_ip(targetPC)
1331-
1332-
case "arm64":
1333-
// Push LR. The injected call is responsible
1334-
// for restoring LR. gentraceback is aware of
1335-
// this extra slot. See sigctxt.pushCall in
1336-
// signal_arm64.go.
1337-
sp := c.sp() - 16 // SP needs 16-byte alignment
1338-
c.set_sp(sp)
1339-
*(*uint64)(unsafe.Pointer(sp)) = uint64(c.lr())
1340-
c.set_lr(newpc)
1341-
c.set_ip(targetPC)
1342-
}
1307+
c.pushCall(targetPC, resumePC)
13431308
stdcall2(_SetThreadContext, thread, uintptr(unsafe.Pointer(c)))
13441309
}
13451310
}

src/runtime/signal_windows.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runtime
66

77
import (
88
"internal/abi"
9-
"internal/runtime/sys"
109
"unsafe"
1110
)
1211

@@ -247,18 +246,11 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
247246
// sigpanic call to make it look like that. Instead, just
248247
// overwrite the PC. (See issue #35773)
249248
if r.ip() != 0 && r.ip() != abi.FuncPCABI0(asyncPreempt) {
250-
sp := unsafe.Pointer(r.sp())
251-
delta := uintptr(sys.StackAlign)
252-
sp = add(sp, -delta)
253-
r.set_sp(uintptr(sp))
254-
if usesLR {
255-
*((*uintptr)(sp)) = r.lr()
256-
r.set_lr(r.ip())
257-
} else {
258-
*((*uintptr)(sp)) = r.ip()
259-
}
249+
r.pushCall(abi.FuncPCABI0(sigpanic0), r.ip())
250+
} else {
251+
// Not safe to push the call. Just clobber the frame.
252+
r.set_ip(abi.FuncPCABI0(sigpanic0))
260253
}
261-
r.set_ip(abi.FuncPCABI0(sigpanic0))
262254
return _EXCEPTION_CONTINUE_EXECUTION
263255
}
264256

0 commit comments

Comments
 (0)