mirror of
https://github.com/golang/go
synced 2024-11-26 09:18:07 -07:00
cmd/internal/obj/x86: simplify huge frame prologue
For stack frames larger than StackBig, the stack split prologue needs to guard against potential wraparound. Currently, it carefully arranges to avoid underflow, but this is complicated and requires a special check for StackPreempt. StackPreempt is no longer the only stack poison value, so this check will incorrectly succeed if the stack bound is poisoned with any other value. This CL simplifies the logic of the check, reduces its length, and accounts for any possible poison value by directly checking for underflow. Change-Id: I917a313102d6a21895ef7c4b0f304fb84b292c81 Reviewed-on: https://go-review.googlesource.com/c/go/+/307010 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
parent
af1789a61c
commit
ef3122e909
@ -1021,6 +1021,12 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
|||||||
sub = ASUBL
|
sub = ASUBL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmp := int16(REG_AX) // use AX for 32-bit
|
||||||
|
if ctxt.Arch.Family == sys.AMD64 {
|
||||||
|
// Avoid register parameters.
|
||||||
|
tmp = int16(REGENTRYTMP0)
|
||||||
|
}
|
||||||
|
|
||||||
var q1 *obj.Prog
|
var q1 *obj.Prog
|
||||||
if framesize <= objabi.StackSmall {
|
if framesize <= objabi.StackSmall {
|
||||||
// small stack: SP <= stackguard
|
// small stack: SP <= stackguard
|
||||||
@ -1043,11 +1049,6 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
|||||||
// unnecessarily. See issue #35470.
|
// unnecessarily. See issue #35470.
|
||||||
p = ctxt.StartUnsafePoint(p, newprog)
|
p = ctxt.StartUnsafePoint(p, newprog)
|
||||||
} else if framesize <= objabi.StackBig {
|
} else if framesize <= objabi.StackBig {
|
||||||
tmp := int16(REG_AX) // use AX for 32-bit
|
|
||||||
if ctxt.Arch.Family == sys.AMD64 {
|
|
||||||
// for 64-bit, stay away from register ABI parameter registers, even w/o GOEXPERIMENT=regabi
|
|
||||||
tmp = int16(REGENTRYTMP0)
|
|
||||||
}
|
|
||||||
// large stack: SP-framesize <= stackguard-StackSmall
|
// large stack: SP-framesize <= stackguard-StackSmall
|
||||||
// LEAQ -xxx(SP), tmp
|
// LEAQ -xxx(SP), tmp
|
||||||
// CMPQ tmp, stackguard
|
// CMPQ tmp, stackguard
|
||||||
@ -1073,77 +1074,51 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
|||||||
|
|
||||||
p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
|
p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
|
||||||
} else {
|
} else {
|
||||||
tmp1 := int16(REG_SI)
|
// Such a large stack we need to protect against underflow.
|
||||||
tmp2 := int16(REG_AX)
|
// The runtime guarantees SP > objabi.StackBig, but
|
||||||
if ctxt.Arch.Family == sys.AMD64 {
|
// framesize is large enough that SP-framesize may
|
||||||
tmp1 = int16(REGENTRYTMP0) // register ABI uses REG_SI and REG_AX for parameters.
|
// underflow, causing a direct comparison with the
|
||||||
tmp2 = int16(REGENTRYTMP1)
|
// stack guard to incorrectly succeed. We explicitly
|
||||||
}
|
// guard against underflow.
|
||||||
// Such a large stack we need to protect against wraparound.
|
|
||||||
// If SP is close to zero:
|
|
||||||
// SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
|
|
||||||
// The +StackGuard on both sides is required to keep the left side positive:
|
|
||||||
// SP is allowed to be slightly below stackguard. See stack.h.
|
|
||||||
//
|
//
|
||||||
// Preemption sets stackguard to StackPreempt, a very large value.
|
// MOVQ SP, tmp
|
||||||
// That breaks the math above, so we have to check for that explicitly.
|
// SUBQ $(framesize - StackSmall), tmp
|
||||||
// MOVQ stackguard, tmp1
|
// // If subtraction wrapped (carry set), morestack.
|
||||||
// CMPQ SI, $StackPreempt
|
// JCS label-of-call-to-morestack
|
||||||
// JEQ label-of-call-to-morestack
|
// CMPQ tmp, stackguard
|
||||||
// LEAQ StackGuard(SP), tmp2
|
|
||||||
// SUBQ tmp1, tmp2
|
|
||||||
// CMPQ tmp2, $(framesize+(StackGuard-StackSmall))
|
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
p = obj.Appendp(p, newprog)
|
||||||
|
|
||||||
p.As = mov
|
p.As = mov
|
||||||
p.From.Type = obj.TYPE_MEM
|
p.From.Type = obj.TYPE_REG
|
||||||
p.From.Reg = rg
|
p.From.Reg = REG_SP
|
||||||
p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
|
|
||||||
if cursym.CFunc() {
|
|
||||||
p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
|
|
||||||
}
|
|
||||||
p.To.Type = obj.TYPE_REG
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Reg = tmp1
|
p.To.Reg = tmp
|
||||||
|
|
||||||
p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
|
p = ctxt.StartUnsafePoint(p, newprog) // see the comment above
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
p = obj.Appendp(p, newprog)
|
||||||
p.As = cmp
|
p.As = sub
|
||||||
p.From.Type = obj.TYPE_REG
|
p.From.Type = obj.TYPE_CONST
|
||||||
p.From.Reg = tmp1
|
p.From.Offset = int64(framesize) - objabi.StackSmall
|
||||||
p.To.Type = obj.TYPE_CONST
|
p.To.Type = obj.TYPE_REG
|
||||||
p.To.Offset = objabi.StackPreempt
|
p.To.Reg = tmp
|
||||||
if ctxt.Arch.Family == sys.I386 {
|
|
||||||
p.To.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
p = obj.Appendp(p, newprog)
|
||||||
p.As = AJEQ
|
p.As = AJCS
|
||||||
p.To.Type = obj.TYPE_BRANCH
|
p.To.Type = obj.TYPE_BRANCH
|
||||||
q1 = p
|
q1 = p
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
|
||||||
p.As = lea
|
|
||||||
p.From.Type = obj.TYPE_MEM
|
|
||||||
p.From.Reg = REG_SP
|
|
||||||
p.From.Offset = int64(objabi.StackGuard)
|
|
||||||
p.To.Type = obj.TYPE_REG
|
|
||||||
p.To.Reg = tmp2
|
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
|
||||||
p.As = sub
|
|
||||||
p.From.Type = obj.TYPE_REG
|
|
||||||
p.From.Reg = tmp1
|
|
||||||
p.To.Type = obj.TYPE_REG
|
|
||||||
p.To.Reg = tmp2
|
|
||||||
|
|
||||||
p = obj.Appendp(p, newprog)
|
p = obj.Appendp(p, newprog)
|
||||||
p.As = cmp
|
p.As = cmp
|
||||||
p.From.Type = obj.TYPE_REG
|
p.From.Type = obj.TYPE_REG
|
||||||
p.From.Reg = tmp2
|
p.From.Reg = tmp
|
||||||
p.To.Type = obj.TYPE_CONST
|
p.To.Type = obj.TYPE_MEM
|
||||||
p.To.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
|
p.To.Reg = rg
|
||||||
|
p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
|
||||||
|
if cursym.CFunc() {
|
||||||
|
p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// common
|
// common
|
||||||
|
@ -92,6 +92,10 @@ const (
|
|||||||
|
|
||||||
// The stack guard is a pointer this many bytes above the
|
// The stack guard is a pointer this many bytes above the
|
||||||
// bottom of the stack.
|
// bottom of the stack.
|
||||||
|
//
|
||||||
|
// The guard leaves enough room for one _StackSmall frame plus
|
||||||
|
// a _StackLimit chain of NOSPLIT calls plus _StackSystem
|
||||||
|
// bytes for the OS.
|
||||||
_StackGuard = 928*sys.StackGuardMultiplier + _StackSystem
|
_StackGuard = 928*sys.StackGuardMultiplier + _StackSystem
|
||||||
|
|
||||||
// After a stack split check the SP is allowed to be this
|
// After a stack split check the SP is allowed to be this
|
||||||
@ -123,15 +127,16 @@ const (
|
|||||||
const (
|
const (
|
||||||
uintptrMask = 1<<(8*sys.PtrSize) - 1
|
uintptrMask = 1<<(8*sys.PtrSize) - 1
|
||||||
|
|
||||||
|
// The values below can be stored to g.stackguard0 to force
|
||||||
|
// the next stack check to fail.
|
||||||
|
// These are all larger than any real SP.
|
||||||
|
|
||||||
// Goroutine preemption request.
|
// Goroutine preemption request.
|
||||||
// Stored into g->stackguard0 to cause split stack check failure.
|
|
||||||
// Must be greater than any real sp.
|
|
||||||
// 0xfffffade in hex.
|
// 0xfffffade in hex.
|
||||||
stackPreempt = uintptrMask & -1314
|
stackPreempt = uintptrMask & -1314
|
||||||
|
|
||||||
// Thread is forking.
|
// Thread is forking. Causes a split stack check failure.
|
||||||
// Stored into g->stackguard0 to cause split stack check failure.
|
// 0xfffffb2e in hex.
|
||||||
// Must be greater than any real sp.
|
|
||||||
stackFork = uintptrMask & -1234
|
stackFork = uintptrMask & -1234
|
||||||
|
|
||||||
// Force a stack movement. Used for debugging.
|
// Force a stack movement. Used for debugging.
|
||||||
|
Loading…
Reference in New Issue
Block a user