runtime: support for injecting calls at signals on x86

This adds a sigctxt.pushCall method that pushes a call at the signaled
site. We'll use this to inject asynchronous preemptions and in some
places we use it to clean up preparePanic.

For the moment this only works on 386 and amd64. We stub it out on
other platforms and will avoid calling the stubbed version.

For #10958, #24543.

Change-Id: I49e0e853f935d32dd67a70c6cafbae44ee68af8e
Reviewed-on: https://go-review.googlesource.com/c/go/+/201758
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Austin Clements 2019-10-14 19:28:58 -04:00
parent 61fa79885b
commit 2d031dc559
8 changed files with 69 additions and 18 deletions

View File

@ -57,14 +57,21 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
sp := uintptr(c.esp())
if shouldPushSigpanic(gp, pc, *(*uintptr)(unsafe.Pointer(sp))) {
// Make it look like the faulting PC called sigpanic.
if sys.RegSize > sys.PtrSize {
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = 0
}
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_esp(uint32(sp))
c.pushCall(funcPC(sigpanic))
} else {
// Not safe to push the call. Just clobber the frame.
c.set_eip(uint32(funcPC(sigpanic)))
}
c.set_eip(uint32(funcPC(sigpanic)))
}
const pushCallSupported = true
func (c *sigctxt) pushCall(targetPC uintptr) {
// Make it look like the signaled instruction called target.
pc := uintptr(c.eip())
sp := uintptr(c.esp())
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_esp(uint32(sp))
c.set_eip(uint32(targetPC))
}

View File

@ -66,14 +66,22 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
sp := uintptr(c.rsp())
if shouldPushSigpanic(gp, pc, *(*uintptr)(unsafe.Pointer(sp))) {
// Make it look the like faulting PC called sigpanic.
if sys.RegSize > sys.PtrSize {
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = 0
}
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_rsp(uint64(sp))
c.pushCall(funcPC(sigpanic))
} else {
// Not safe to push the call. Just clobber the frame.
c.set_rip(uint64(funcPC(sigpanic)))
}
c.set_rip(uint64(funcPC(sigpanic)))
}
// TODO: Remove pushCallSupported once all platforms support it.
const pushCallSupported = true
func (c *sigctxt) pushCall(targetPC uintptr) {
// Make it look like the signaled instruction called target.
pc := uintptr(c.rip())
sp := uintptr(c.rsp())
sp -= sys.PtrSize
*(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_rsp(uint64(sp))
c.set_rip(uint64(targetPC))
}

View File

@ -62,3 +62,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r10(uint32(uintptr(unsafe.Pointer(gp))))
c.set_pc(uint32(funcPC(sigpanic)))
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}

View File

@ -78,3 +78,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r28(uint64(uintptr(unsafe.Pointer(gp))))
c.set_pc(uint64(funcPC(sigpanic)))
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}

View File

@ -109,3 +109,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r13(uint64(uintptr(unsafe.Pointer(gp))))
c.set_pc(uint64(funcPC(sigpanic)))
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}

View File

@ -84,3 +84,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r30(uint64(uintptr(unsafe.Pointer(gp))))
c.set_pc(sigpanicPC)
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}

View File

@ -79,3 +79,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r30(uint32(uintptr(unsafe.Pointer(gp))))
c.set_pc(uint32(funcPC(sigpanic)))
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}

View File

@ -85,3 +85,9 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
c.set_r12(uint64(funcPC(sigpanic)))
c.set_pc(uint64(funcPC(sigpanic)))
}
const pushCallSupported = false
func (c *sigctxt) pushCall(targetPC uintptr) {
throw("not implemented")
}