From 8714e39497dba141ce7ed83c6a18c3c0def66e77 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 14 Oct 2019 17:05:56 -0400 Subject: [PATCH] runtime: M-targeted signals for BSDs For these, we split up the existing runtime.raise assembly implementation into its constituent "get thread ID" and "signal thread" parts. This lets us implement signalM and reimplement raise in pure Go. (NetBSD conveniently already had lwp_self.) We also change minit to store the procid directly, rather than depending on newosproc to do so. This is because newosproc isn't called for the bootstrap M, but we need a procid for every M. This is also simpler overall. For #10958, #24543. Change-Id: Ie5f1fcada6a33046375066bcbe054d1f784d39c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/201402 Run-TryBot: Austin Clements Reviewed-by: Cherry Zhang --- src/runtime/defs_freebsd_386.go | 2 ++ src/runtime/defs_freebsd_amd64.go | 2 ++ src/runtime/defs_freebsd_arm.go | 2 ++ src/runtime/defs_freebsd_arm64.go | 2 ++ src/runtime/os_dragonfly.go | 25 +++++++++++++++++++------ src/runtime/os_freebsd.go | 29 ++++++++++++++++++++--------- src/runtime/os_netbsd.go | 17 ++++++++++++++++- src/runtime/os_openbsd.go | 19 +++++++++++++------ src/runtime/sys_dragonfly_amd64.s | 12 ++++++++---- src/runtime/sys_freebsd_386.s | 15 +++++++-------- src/runtime/sys_freebsd_amd64.s | 15 +++++++++------ src/runtime/sys_freebsd_arm.s | 15 +++++++++------ src/runtime/sys_freebsd_arm64.s | 16 +++++++++++----- src/runtime/sys_netbsd_386.s | 7 +++---- src/runtime/sys_netbsd_amd64.s | 8 +++----- src/runtime/sys_netbsd_arm.s | 6 +++--- src/runtime/sys_netbsd_arm64.s | 7 +++---- src/runtime/sys_openbsd_386.s | 9 +++++++-- src/runtime/sys_openbsd_amd64.s | 10 +++++++--- src/runtime/sys_openbsd_arm.s | 10 +++++++--- src/runtime/sys_openbsd_arm64.s | 10 +++++++--- 21 files changed, 160 insertions(+), 78 deletions(-) diff --git a/src/runtime/defs_freebsd_386.go b/src/runtime/defs_freebsd_386.go index 6294fc32d4..767755425c 100644 --- a/src/runtime/defs_freebsd_386.go +++ b/src/runtime/defs_freebsd_386.go @@ -126,6 +126,8 @@ type thrparam struct { spare [3]uintptr } +type thread int32 // long + type sigset struct { __bits [4]uint32 } diff --git a/src/runtime/defs_freebsd_amd64.go b/src/runtime/defs_freebsd_amd64.go index 840c710eeb..5a833426fd 100644 --- a/src/runtime/defs_freebsd_amd64.go +++ b/src/runtime/defs_freebsd_amd64.go @@ -127,6 +127,8 @@ type thrparam struct { spare [3]uintptr } +type thread int64 // long + type sigset struct { __bits [4]uint32 } diff --git a/src/runtime/defs_freebsd_arm.go b/src/runtime/defs_freebsd_arm.go index 3307c8bbae..b55dfd88cf 100644 --- a/src/runtime/defs_freebsd_arm.go +++ b/src/runtime/defs_freebsd_arm.go @@ -126,6 +126,8 @@ type thrparam struct { spare [3]uintptr } +type thread int32 // long + type sigset struct { __bits [4]uint32 } diff --git a/src/runtime/defs_freebsd_arm64.go b/src/runtime/defs_freebsd_arm64.go index 3eebe5dbb3..5b9d504ba6 100644 --- a/src/runtime/defs_freebsd_arm64.go +++ b/src/runtime/defs_freebsd_arm64.go @@ -127,6 +127,8 @@ type thrparam struct { spare [3]uintptr } +type thread int64 // long + type sigset struct { __bits [4]uint32 } diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go index 3266b2623a..6578fcbeb1 100644 --- a/src/runtime/os_dragonfly.go +++ b/src/runtime/os_dragonfly.go @@ -38,9 +38,11 @@ func setitimer(mode int32, new, old *itimerval) //go:noescape func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 -func raise(sig uint32) func raiseproc(sig uint32) +func lwp_gettid() int32 +func lwp_kill(pid, tid int32, sig int) + //go:noescape func sys_umtx_sleep(addr *uint32, val, timeout int32) int32 @@ -151,7 +153,7 @@ func newosproc(mp *m) { start_func: funcPC(lwp_start), arg: unsafe.Pointer(mp), stack: uintptr(stk), - tid1: unsafe.Pointer(&mp.procid), + tid1: nil, // minit will record tid tid2: nil, } @@ -191,10 +193,7 @@ func mpreinit(mp *m) { // Called to initialize a new m (including the bootstrap m). // Called on the new thread, cannot allocate memory. func minit() { - // m.procid is a uint64, but lwp_start writes an int32. Fix it up. - _g_ := getg() - _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) - + getg().m.procid = uint64(lwp_gettid()) minitSignals() } @@ -288,3 +287,17 @@ func sysauxv(auxv []uintptr) { } } } + +// raise sends a signal to the calling thread. +// +// It must be nosplit because it is used by the signal handler before +// it definitely has a Go stack. +// +//go:nosplit +func raise(sig uint32) { + lwp_kill(-1, lwp_gettid(), int(sig)) +} + +func signalM(mp *m, sig int) { + lwp_kill(-1, int32(mp.procid), sig) +} diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go index 183d8ab9c7..69e05b66a2 100644 --- a/src/runtime/os_freebsd.go +++ b/src/runtime/os_freebsd.go @@ -26,9 +26,11 @@ func setitimer(mode int32, new, old *itimerval) //go:noescape func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 -func raise(sig uint32) func raiseproc(sig uint32) +func thr_self() thread +func thr_kill(tid thread, sig int) + //go:noescape func sys_umtx_op(addr *uint32, mode int32, val uint32, uaddr1 uintptr, ut *umtx_time) int32 @@ -195,7 +197,7 @@ func newosproc(mp *m) { arg: unsafe.Pointer(mp), stack_base: mp.g0.stack.lo, stack_size: uintptr(stk) - mp.g0.stack.lo, - child_tid: unsafe.Pointer(&mp.procid), + child_tid: nil, // minit will record tid parent_tid: nil, tls_base: unsafe.Pointer(&mp.tls[0]), tls_size: unsafe.Sizeof(mp.tls), @@ -231,7 +233,7 @@ func newosproc0(stacksize uintptr, fn unsafe.Pointer) { arg: nil, stack_base: uintptr(stack), //+stacksize? stack_size: stacksize, - child_tid: unsafe.Pointer(&m0.procid), + child_tid: nil, // minit will record tid parent_tid: nil, tls_base: unsafe.Pointer(&m0.tls[0]), tls_size: unsafe.Sizeof(m0.tls), @@ -290,12 +292,7 @@ func mpreinit(mp *m) { // Called to initialize a new m (including the bootstrap m). // Called on the new thread, cannot allocate memory. func minit() { - // m.procid is a uint64, but thr_new writes a uint32 on 32-bit systems. - // Fix it up. (Only matters on big-endian, but be clean anyway.) - if sys.PtrSize == 4 { - _g_ := getg() - _g_.m.procid = uint64(*(*uint32)(unsafe.Pointer(&_g_.m.procid))) - } + getg().m.procid = uint64(thr_self()) // On FreeBSD before about April 2017 there was a bug such // that calling execve from a thread other than the main @@ -423,3 +420,17 @@ func sysSigaction(sig uint32, new, old *sigactiont) { // asmSigaction is implemented in assembly. //go:noescape func asmSigaction(sig uintptr, new, old *sigactiont) int32 + +// raise sends a signal to the calling thread. +// +// It must be nosplit because it is used by the signal handler before +// it definitely has a Go stack. +// +//go:nosplit +func raise(sig uint32) { + thr_kill(thr_self(), int(sig)) +} + +func signalM(mp *m, sig int) { + thr_kill(thread(mp.procid), sig) +} diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go index 3cb9411a9c..b50cf237fb 100644 --- a/src/runtime/os_netbsd.go +++ b/src/runtime/os_netbsd.go @@ -47,9 +47,10 @@ func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, nds func lwp_tramp() -func raise(sig uint32) func raiseproc(sig uint32) +func lwp_kill(tid int32, sig int) + //go:noescape func getcontext(ctxt unsafe.Pointer) @@ -361,3 +362,17 @@ func sysauxv(auxv []uintptr) { } } } + +// raise sends signal to the calling thread. +// +// It must be nosplit because it is used by the signal handler before +// it definitely has a Go stack. +// +//go:nosplit +func raise(sig uint32) { + lwp_kill(lwp_self(), int(sig)) +} + +func signalM(mp *m, sig int) { + lwp_kill(int32(mp.procid), sig) +} diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index 351a99f7e9..f26b39575d 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -42,9 +42,11 @@ func sigprocmask(how int32, new, old *sigset) { //go:noescape func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 -func raise(sig uint32) func raiseproc(sig uint32) +func getthrid() int32 +func thrkill(tid int32, sig int) + //go:noescape func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32 @@ -190,7 +192,7 @@ func newosproc(mp *m) { // rather than at the top of it. param := tforkt{ tf_tcb: unsafe.Pointer(&mp.tls[0]), - tf_tid: (*int32)(unsafe.Pointer(&mp.procid)), + tf_tid: nil, // minit will record tid tf_stack: uintptr(stk) - sys.PtrSize, } @@ -238,10 +240,7 @@ func mpreinit(mp *m) { // Called to initialize a new m (including the bootstrap m). // Called on the new thread, can not allocate memory. func minit() { - // m.procid is a uint64, but tfork writes an int32. Fix it up. - _g_ := getg() - _g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid))) - + getg().m.procid = uint64(getthrid()) minitSignals() } @@ -337,3 +336,11 @@ func osStackRemap(s *mspan, flags int32) { throw("remapping stack memory failed") } } + +func raise(sig uint32) { + thrkill(getthrid(), int(sig)) +} + +func signalM(mp *m, sig int) { + thrkill(int32(mp.procid), sig) +} diff --git a/src/runtime/sys_dragonfly_amd64.s b/src/runtime/sys_dragonfly_amd64.s index 68962d9e30..580633af55 100644 --- a/src/runtime/sys_dragonfly_amd64.s +++ b/src/runtime/sys_dragonfly_amd64.s @@ -134,12 +134,16 @@ TEXT runtime·write1(SB),NOSPLIT,$-8 MOVL AX, ret+24(FP) RET -TEXT runtime·raise(SB),NOSPLIT,$16 +TEXT runtime·lwp_gettid(SB),NOSPLIT,$0-4 MOVL $496, AX // lwp_gettid SYSCALL - MOVQ $-1, DI // arg 1 - pid - MOVQ AX, SI // arg 2 - tid - MOVL sig+0(FP), DX // arg 3 - signum + MOVL AX, ret+0(FP) + RET + +TEXT runtime·lwp_kill(SB),NOSPLIT,$0-16 + MOVL pid+0(FP), DI // arg 1 - pid + MOVL tid+4(FP), SI // arg 2 - tid + MOVQ sig+8(FP), DX // arg 3 - signum MOVL $497, AX // lwp_kill SYSCALL RET diff --git a/src/runtime/sys_freebsd_386.s b/src/runtime/sys_freebsd_386.s index 48f64b9f8b..c346e719e1 100644 --- a/src/runtime/sys_freebsd_386.s +++ b/src/runtime/sys_freebsd_386.s @@ -131,17 +131,16 @@ TEXT runtime·write1(SB),NOSPLIT,$-4 MOVL AX, ret+12(FP) RET -TEXT runtime·raise(SB),NOSPLIT,$16 - // thr_self(&8(SP)) - LEAL 8(SP), AX +TEXT runtime·thr_self(SB),NOSPLIT,$8-4 + // thr_self(&0(FP)) + LEAL ret+0(FP), AX MOVL AX, 4(SP) MOVL $432, AX INT $0x80 - // thr_kill(self, SIGPIPE) - MOVL 8(SP), AX - MOVL AX, 4(SP) - MOVL sig+0(FP), AX - MOVL AX, 8(SP) + RET + +TEXT runtime·thr_kill(SB),NOSPLIT,$-4 + // thr_kill(tid, sig) MOVL $433, AX INT $0x80 RET diff --git a/src/runtime/sys_freebsd_amd64.s b/src/runtime/sys_freebsd_amd64.s index d24ab1f643..010b2ec4d4 100644 --- a/src/runtime/sys_freebsd_amd64.s +++ b/src/runtime/sys_freebsd_amd64.s @@ -132,14 +132,17 @@ TEXT runtime·write1(SB),NOSPLIT,$-8 MOVL AX, ret+24(FP) RET -TEXT runtime·raise(SB),NOSPLIT,$16 - // thr_self(&8(SP)) - LEAQ 8(SP), DI // arg 1 &8(SP) +TEXT runtime·thr_self(SB),NOSPLIT,$0-8 + // thr_self(&0(FP)) + LEAQ ret+0(FP), DI // arg 1 MOVL $432, AX SYSCALL - // thr_kill(self, SIGPIPE) - MOVQ 8(SP), DI // arg 1 id - MOVL sig+0(FP), SI // arg 2 + RET + +TEXT runtime·thr_kill(SB),NOSPLIT,$0-16 + // thr_kill(tid, sig) + MOVQ tid+0(FP), DI // arg 1 id + MOVQ sig+8(FP), SI // arg 2 sig MOVL $433, AX SYSCALL RET diff --git a/src/runtime/sys_freebsd_arm.s b/src/runtime/sys_freebsd_arm.s index 8da36dff17..8dcdbb56bd 100644 --- a/src/runtime/sys_freebsd_arm.s +++ b/src/runtime/sys_freebsd_arm.s @@ -165,14 +165,17 @@ TEXT runtime·closefd(SB),NOSPLIT|NOFRAME,$0 MOVW R0, ret+4(FP) RET -TEXT runtime·raise(SB),NOSPLIT,$8 - // thr_self(&4(R13)) - MOVW $4(R13), R0 // arg 1 &4(R13) +TEXT runtime·thr_self(SB),NOSPLIT,$0-4 + // thr_self(&0(FP)) + MOVW $ret+0(FP), R0 // arg 1 MOVW $SYS_thr_self, R7 SWI $0 - // thr_kill(self, SIGPIPE) - MOVW 4(R13), R0 // arg 1 id - MOVW sig+0(FP), R1 // arg 2 - signal + RET + +TEXT runtime·thr_kill(SB),NOSPLIT,$0-8 + // thr_kill(tid, sig) + MOVW tid+0(FP), R0 // arg 1 id + MOVW sig+4(FP), R1 // arg 2 signal MOVW $SYS_thr_kill, R7 SWI $0 RET diff --git a/src/runtime/sys_freebsd_arm64.s b/src/runtime/sys_freebsd_arm64.s index ca2ea4f1d6..e0ef2f679d 100644 --- a/src/runtime/sys_freebsd_arm64.s +++ b/src/runtime/sys_freebsd_arm64.s @@ -197,13 +197,19 @@ TEXT runtime·usleep(SB),NOSPLIT,$24-4 SVC RET -// func raise(sig uint32) -TEXT runtime·raise(SB),NOSPLIT,$8 - MOVD $8(RSP), R0 // arg 1 &8(RSP) +// func thr_self() thread +TEXT runtime·thr_self(SB),NOSPLIT,$8-8 + MOVD $ptr-8(SP), R0 // arg 1 &8(SP) MOVD $SYS_thr_self, R8 SVC - MOVD 8(RSP), R0 // arg 1 pid - MOVW sig+0(FP), R1 + MOVD ptr-8(SP), R0 + MOVD R0, ret+0(FP) + RET + +// func thr_kill(t thread, sig int) +TEXT runtime·thr_kill(SB),NOSPLIT,$0-16 + MOVD tid+0(FP), R0 // arg 1 pid + MOVD sig+8(FP), R1 // arg 2 sig MOVD $SYS_thr_kill, R8 SVC RET diff --git a/src/runtime/sys_netbsd_386.s b/src/runtime/sys_netbsd_386.s index 7a542da526..d0c470c457 100644 --- a/src/runtime/sys_netbsd_386.s +++ b/src/runtime/sys_netbsd_386.s @@ -140,12 +140,11 @@ TEXT runtime·usleep(SB),NOSPLIT,$24 INT $0x80 RET -TEXT runtime·raise(SB),NOSPLIT,$12 - MOVL $SYS__lwp_self, AX - INT $0x80 +TEXT runtime·lwp_kill(SB),NOSPLIT,$12-8 MOVL $0, 0(SP) + MOVL tid+0(FP), AX MOVL AX, 4(SP) // arg 1 - target - MOVL sig+0(FP), AX + MOVL sig+4(FP), AX MOVL AX, 8(SP) // arg 2 - signo MOVL $SYS__lwp_kill, AX INT $0x80 diff --git a/src/runtime/sys_netbsd_amd64.s b/src/runtime/sys_netbsd_amd64.s index 4d1d36f01b..dc9bd127d2 100644 --- a/src/runtime/sys_netbsd_amd64.s +++ b/src/runtime/sys_netbsd_amd64.s @@ -209,11 +209,9 @@ TEXT runtime·usleep(SB),NOSPLIT,$16 SYSCALL RET -TEXT runtime·raise(SB),NOSPLIT,$16 - MOVL $SYS__lwp_self, AX - SYSCALL - MOVQ AX, DI // arg 1 - target - MOVL sig+0(FP), SI // arg 2 - signo +TEXT runtime·lwp_kill(SB),NOSPLIT,$0-16 + MOVL tid+0(FP), DI // arg 1 - target + MOVQ sig+8(FP), SI // arg 2 - signo MOVL $SYS__lwp_kill, AX SYSCALL RET diff --git a/src/runtime/sys_netbsd_arm.s b/src/runtime/sys_netbsd_arm.s index c8ee262d59..64428bee4d 100644 --- a/src/runtime/sys_netbsd_arm.s +++ b/src/runtime/sys_netbsd_arm.s @@ -193,9 +193,9 @@ TEXT runtime·usleep(SB),NOSPLIT,$16 SWI $SYS___nanosleep50 RET -TEXT runtime·raise(SB),NOSPLIT,$16 - SWI $SYS__lwp_self // the returned R0 is arg 1 - MOVW sig+0(FP), R1 // arg 2 - signal +TEXT runtime·lwp_kill(SB),NOSPLIT,$0-8 + MOVW tid+0(FP), R0 // arg 1 - tid + MOVW sig+4(FP), R1 // arg 2 - signal SWI $SYS__lwp_kill RET diff --git a/src/runtime/sys_netbsd_arm64.s b/src/runtime/sys_netbsd_arm64.s index ccc34142aa..e70be0fa74 100644 --- a/src/runtime/sys_netbsd_arm64.s +++ b/src/runtime/sys_netbsd_arm64.s @@ -205,10 +205,9 @@ TEXT runtime·usleep(SB),NOSPLIT,$24-4 SVC $SYS___nanosleep50 RET -TEXT runtime·raise(SB),NOSPLIT,$16 - SVC $SYS__lwp_self - // arg 1 - target (lwp_self) - MOVW sig+0(FP), R1 // arg 2 - signo +TEXT runtime·lwp_kill(SB),NOSPLIT,$0-16 + MOVW tid+0(FP), R0 // arg 1 - target + MOVD sig+8(FP), R1 // arg 2 - signo SVC $SYS__lwp_kill RET diff --git a/src/runtime/sys_openbsd_386.s b/src/runtime/sys_openbsd_386.s index 9805a43802..24fbfd6266 100644 --- a/src/runtime/sys_openbsd_386.s +++ b/src/runtime/sys_openbsd_386.s @@ -97,12 +97,17 @@ TEXT runtime·usleep(SB),NOSPLIT,$24 INT $0x80 RET -TEXT runtime·raise(SB),NOSPLIT,$16 +TEXT runtime·getthrid(SB),NOSPLIT,$0-4 MOVL $299, AX // sys_getthrid INT $0x80 + MOVL AX, ret+0(FP) + RET + +TEXT runtime·thrkill(SB),NOSPLIT,$16-8 MOVL $0, 0(SP) + MOVL tid+0(FP), AX MOVL AX, 4(SP) // arg 1 - tid - MOVL sig+0(FP), AX + MOVL sig+4(FP), AX MOVL AX, 8(SP) // arg 2 - signum MOVL $0, 12(SP) // arg 3 - tcb MOVL $119, AX // sys_thrkill diff --git a/src/runtime/sys_openbsd_amd64.s b/src/runtime/sys_openbsd_amd64.s index 66526bff0d..37d70ab9aa 100644 --- a/src/runtime/sys_openbsd_amd64.s +++ b/src/runtime/sys_openbsd_amd64.s @@ -171,11 +171,15 @@ TEXT runtime·usleep(SB),NOSPLIT,$16 SYSCALL RET -TEXT runtime·raise(SB),NOSPLIT,$16 +TEXT runtime·getthrid(SB),NOSPLIT,$0-4 MOVL $299, AX // sys_getthrid SYSCALL - MOVQ AX, DI // arg 1 - tid - MOVL sig+0(FP), SI // arg 2 - signum + MOVL AX, ret+0(FP) + RET + +TEXT runtime·thrkill(SB),NOSPLIT,$0-16 + MOVL tid+0(FP), DI // arg 1 - tid + MOVQ sig+8(FP), SI // arg 2 - signum MOVQ $0, DX // arg 3 - tcb MOVL $119, AX // sys_thrkill SYSCALL diff --git a/src/runtime/sys_openbsd_arm.s b/src/runtime/sys_openbsd_arm.s index 92ab3270be..2177a7308c 100644 --- a/src/runtime/sys_openbsd_arm.s +++ b/src/runtime/sys_openbsd_arm.s @@ -102,11 +102,15 @@ TEXT runtime·usleep(SB),NOSPLIT,$16 SWI $0 RET -TEXT runtime·raise(SB),NOSPLIT,$12 +TEXT runtime·getthrid(SB),NOSPLIT,$0-4 MOVW $299, R12 // sys_getthrid SWI $0 - // arg 1 - tid, already in R0 - MOVW sig+0(FP), R1 // arg 2 - signum + MOVW R0, ret+0(FP) + RET + +TEXT runtime·thrkill(SB),NOSPLIT,$0-8 + MOVW tid+0(FP), R0 // arg 1 - tid + MOVW sig+4(FP), R1 // arg 2 - signum MOVW $0, R2 // arg 3 - tcb MOVW $119, R12 // sys_thrkill SWI $0 diff --git a/src/runtime/sys_openbsd_arm64.s b/src/runtime/sys_openbsd_arm64.s index c8bf2d345e..8e1a5bc542 100644 --- a/src/runtime/sys_openbsd_arm64.s +++ b/src/runtime/sys_openbsd_arm64.s @@ -114,11 +114,15 @@ TEXT runtime·usleep(SB),NOSPLIT,$24-4 SVC RET -TEXT runtime·raise(SB),NOSPLIT,$0 +TEXT runtime·getthrid(SB),NOSPLIT,$0-4 MOVD $299, R8 // sys_getthrid SVC - // arg 1 - tid, already in R0 - MOVW sig+0(FP), R1 // arg 2 - signum + MOVW R0, ret+0(FP) + RET + +TEXT runtime·thrkill(SB),NOSPLIT,$0-16 + MOVW tid+0(FP), R0 // arg 1 - tid + MOVD sig+8(FP), R1 // arg 2 - signum MOVW $0, R2 // arg 3 - tcb MOVD $119, R8 // sys_thrkill SVC -- 2.50.0