Skip to content

Commit 4356e7e

Browse files
committed
runtime: account for spill slots in Windows callback compilation
The Go ABI, as it stands, requires spill space to be reserved for register arguments. syscall.NewCallback (because of compileCallback) does not actually reserve this space, leading to issues if the Go code it invokes actually makes use of it. Fixes #46301. Change-Id: Idbc3578accaaaa29e4ba32291ef08d464da0b7b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/322029 Trust: Michael Knyszek <[email protected]> Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Egon Elbre <[email protected]> Reviewed-by: Cherry Mui <[email protected]>
1 parent 52d7033 commit 4356e7e

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

src/runtime/syscall_windows.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type abiDesc struct {
6464

6565
srcStackSize uintptr // stdcall/fastcall stack space tracking
6666
dstStackSize uintptr // Go stack space used
67+
dstSpill uintptr // Extra stack space for argument spill slots
6768
dstRegisters int // Go ABI int argument registers used
6869

6970
// retOffset is the offset of the uintptr-sized result in the Go
@@ -110,7 +111,14 @@ func (p *abiDesc) assignArg(t *_type) {
110111
// arguments. The same is true on arm.
111112

112113
oldParts := p.parts
113-
if !p.tryRegAssignArg(t, 0) {
114+
if p.tryRegAssignArg(t, 0) {
115+
// Account for spill space.
116+
//
117+
// TODO(mknyszek): Remove this when we no longer have
118+
// caller reserved spill space.
119+
p.dstSpill = alignUp(p.dstSpill, uintptr(t.align))
120+
p.dstSpill += t.size
121+
} else {
114122
// Register assignment failed.
115123
// Undo the work and stack assign.
116124
p.parts = oldParts
@@ -277,7 +285,11 @@ func compileCallback(fn eface, cdecl bool) (code uintptr) {
277285
abiMap.dstStackSize += sys.PtrSize
278286
}
279287

280-
if abiMap.dstStackSize > callbackMaxFrame {
288+
// TODO(mknyszek): Remove dstSpill from this calculation when we no longer have
289+
// caller reserved spill space.
290+
frameSize := alignUp(abiMap.dstStackSize, sys.PtrSize)
291+
frameSize += abiMap.dstSpill
292+
if frameSize > callbackMaxFrame {
281293
panic("compileCallback: function argument frame too large")
282294
}
283295

@@ -356,9 +368,14 @@ func callbackWrap(a *callbackArgs) {
356368
}
357369
}
358370

371+
// TODO(mknyszek): Remove this when we no longer have
372+
// caller reserved spill space.
373+
frameSize := alignUp(c.abiMap.dstStackSize, sys.PtrSize)
374+
frameSize += c.abiMap.dstSpill
375+
359376
// Even though this is copying back results, we can pass a nil
360377
// type because those results must not require write barriers.
361-
reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.abiMap.dstStackSize), uint32(c.abiMap.retOffset), uint32(c.abiMap.dstStackSize), &regs)
378+
reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.abiMap.dstStackSize), uint32(c.abiMap.retOffset), uint32(frameSize), &regs)
362379

363380
// Extract the result.
364381
//

src/runtime/syscall_windows_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ var cbFuncs = []cbFunc{
389389
{func(i1, i2, i3, i4, i5 uint8Pair) uintptr {
390390
return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
391391
}},
392+
{func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
393+
runtime.GC()
394+
return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
395+
}},
392396
}
393397

394398
//go:registerparams
@@ -461,6 +465,16 @@ func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr {
461465
return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
462466
}
463467

468+
// This test forces a GC. The idea is to have enough arguments
469+
// that insufficient spill slots allocated (according to the ABI)
470+
// may cause compiler-generated spills to clobber the return PC.
471+
// Then, the GC stack scanning will catch that.
472+
//go:registerparams
473+
func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
474+
runtime.GC()
475+
return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
476+
}
477+
464478
// TODO(register args): Remove this once we switch to using the register
465479
// calling convention by default, since this is redundant with the existing
466480
// tests.
@@ -479,6 +493,7 @@ var cbFuncsRegABI = []cbFunc{
479493
{sum9int8},
480494
{sum5mix},
481495
{sum5andPair},
496+
{sum9andGC},
482497
}
483498

484499
func getCallbackTestFuncs() []cbFunc {

0 commit comments

Comments
 (0)