Commit 872b168f authored by Ian Lance Taylor's avatar Ian Lance Taylor

runtime: if we don't handle a signal on a non-Go thread, raise it

In the past badsignal would crash the program.  In
https://golang.org/cl/10757044 badsignal was changed to call sigsend,
to fix issue #3250.  The effect of this was that when a non-Go thread
received a signal, and os/signal.Notify was not being used to check
for occurrences of the signal, the signal was ignored.

This changes the code so that if os/signal.Notify is not being used,
then the signal handler is reset to what it was, and the signal is
raised again.  This lets non-Go threads handle the signal as they
wish.  In particular, it means that a segmentation violation in a
non-Go thread will ordinarily crash the process, as it should.

Fixes #10139.
Update #11794.

Change-Id: I2109444aaada9d963ad03b1d071ec667760515e5
Reviewed-on: https://go-review.googlesource.com/12503Reviewed-by: default avatarRuss Cox <rsc@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
parent 428ed1e3
...@@ -82,6 +82,19 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) { ...@@ -82,6 +82,19 @@ func TestCgoExternalThreadSIGPROF(t *testing.T) {
} }
} }
func TestCgoExternalThreadSignal(t *testing.T) {
// issue 10139
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("no pthreads on %s", runtime.GOOS)
}
got := executeTest(t, cgoExternalThreadSignalSource, nil)
want := "OK\n"
if got != want {
t.Fatalf("expected %q, but got %q", want, got)
}
}
func TestCgoDLLImports(t *testing.T) { func TestCgoDLLImports(t *testing.T) {
// test issue 9356 // test issue 9356
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
...@@ -282,6 +295,60 @@ func main() { ...@@ -282,6 +295,60 @@ func main() {
} }
` `
const cgoExternalThreadSignalSource = `
package main
/*
#include <pthread.h>
void **nullptr;
void *crash(void *p) {
*nullptr = p;
return 0;
}
int start_crashing_thread(void) {
pthread_t tid;
return pthread_create(&tid, 0, crash, 0);
}
*/
import "C"
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
if len(os.Args) > 1 && os.Args[1] == "crash" {
i := C.start_crashing_thread()
if i != 0 {
fmt.Println("pthread_create failed:", i)
// Exit with 0 because parent expects us to crash.
return
}
// We should crash immediately, but give it plenty of
// time before failing (by exiting 0) in case we are
// running on a slow system.
time.Sleep(5 * time.Second)
return
}
out, err := exec.Command(os.Args[0], "crash").CombinedOutput()
if err == nil {
fmt.Println("C signal did not crash as expected\n")
fmt.Printf("%s\n", out)
os.Exit(1)
}
fmt.Println("OK")
}
`
const cgoDLLImportsMainSource = ` const cgoDLLImportsMainSource = `
package main package main
......
...@@ -469,3 +469,8 @@ func signalstack(s *stack) { ...@@ -469,3 +469,8 @@ func signalstack(s *stack) {
func updatesigmask(m sigmask) { func updatesigmask(m sigmask) {
sigprocmask(_SIG_SETMASK, &m[0], nil) sigprocmask(_SIG_SETMASK, &m[0], nil)
} }
func unblocksig(sig int32) {
mask := uint32(1) << (uint32(sig) - 1)
sigprocmask(_SIG_UNBLOCK, &mask, nil)
}
...@@ -78,7 +78,7 @@ func newosproc(mp *m, stk unsafe.Pointer) { ...@@ -78,7 +78,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
} }
var oset sigset var oset sigset
sigprocmask(&sigset_all, &oset) sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
params := lwpparams{ params := lwpparams{
start_func: funcPC(lwp_start), start_func: funcPC(lwp_start),
...@@ -91,7 +91,7 @@ func newosproc(mp *m, stk unsafe.Pointer) { ...@@ -91,7 +91,7 @@ func newosproc(mp *m, stk unsafe.Pointer) {
mp.tls[0] = uintptr(mp.id) // XXX so 386 asm can find it mp.tls[0] = uintptr(mp.id) // XXX so 386 asm can find it
lwp_create(&params) lwp_create(&params)
sigprocmask(&oset, nil) sigprocmask(_SIG_SETMASK, &oset, nil)
} }
func osinit() { func osinit() {
...@@ -124,7 +124,7 @@ func msigsave(mp *m) { ...@@ -124,7 +124,7 @@ func msigsave(mp *m) {
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) { if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
throw("insufficient storage for signal mask") throw("insufficient storage for signal mask")
} }
sigprocmask(nil, smask) sigprocmask(_SIG_SETMASK, nil, smask)
} }
// Called to initialize a new m (including the bootstrap m). // Called to initialize a new m (including the bootstrap m).
...@@ -145,14 +145,14 @@ func minit() { ...@@ -145,14 +145,14 @@ func minit() {
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
} }
} }
sigprocmask(&nmask, nil) sigprocmask(_SIG_SETMASK, &nmask, nil)
} }
// Called from dropm to undo the effect of an minit. // Called from dropm to undo the effect of an minit.
func unminit() { func unminit() {
_g_ := getg() _g_ := getg()
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask)) smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
sigprocmask(smask, nil) sigprocmask(_SIG_SETMASK, smask, nil)
signalstack(nil) signalstack(nil)
} }
...@@ -237,5 +237,11 @@ func signalstack(s *stack) { ...@@ -237,5 +237,11 @@ func signalstack(s *stack) {
func updatesigmask(m sigmask) { func updatesigmask(m sigmask) {
var mask sigset var mask sigset
copy(mask.__bits[:], m[:]) copy(mask.__bits[:], m[:])
sigprocmask(&mask, nil) sigprocmask(_SIG_SETMASK, &mask, nil)
}
func unblocksig(sig int32) {
var mask sigset
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
sigprocmask(_SIG_UNBLOCK, &mask, nil)
} }
...@@ -88,9 +88,9 @@ func newosproc(mp *m, stk unsafe.Pointer) { ...@@ -88,9 +88,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
mp.tls[0] = uintptr(mp.id) // so 386 asm can find it mp.tls[0] = uintptr(mp.id) // so 386 asm can find it
var oset sigset var oset sigset
sigprocmask(&sigset_all, &oset) sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
thr_new(&param, int32(unsafe.Sizeof(param))) thr_new(&param, int32(unsafe.Sizeof(param)))
sigprocmask(&oset, nil) sigprocmask(_SIG_SETMASK, &oset, nil)
} }
func osinit() { func osinit() {
...@@ -123,7 +123,7 @@ func msigsave(mp *m) { ...@@ -123,7 +123,7 @@ func msigsave(mp *m) {
if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) { if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
throw("insufficient storage for signal mask") throw("insufficient storage for signal mask")
} }
sigprocmask(nil, smask) sigprocmask(_SIG_SETMASK, nil, smask)
} }
// Called to initialize a new m (including the bootstrap m). // Called to initialize a new m (including the bootstrap m).
...@@ -147,14 +147,14 @@ func minit() { ...@@ -147,14 +147,14 @@ func minit() {
nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31) nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
} }
} }
sigprocmask(&nmask, nil) sigprocmask(_SIG_SETMASK, &nmask, nil)
} }
// Called from dropm to undo the effect of an minit. // Called from dropm to undo the effect of an minit.
func unminit() { func unminit() {
_g_ := getg() _g_ := getg()
smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask)) smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
sigprocmask(smask, nil) sigprocmask(_SIG_SETMASK, smask, nil)
signalstack(nil) signalstack(nil)
} }
...@@ -239,5 +239,11 @@ func signalstack(s *stack) { ...@@ -239,5 +239,11 @@ func signalstack(s *stack) {
func updatesigmask(m [(_NSIG + 31) / 32]uint32) { func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
var mask sigset var mask sigset
copy(mask.__bits[:], m[:]) copy(mask.__bits[:], m[:])
sigprocmask(&mask, nil) sigprocmask(_SIG_SETMASK, &mask, nil)
}
func unblocksig(sig int32) {
var mask sigset
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
sigprocmask(_SIG_UNBLOCK, &mask, nil)
} }
...@@ -333,3 +333,9 @@ func updatesigmask(m sigmask) { ...@@ -333,3 +333,9 @@ func updatesigmask(m sigmask) {
copy(mask[:], m[:]) copy(mask[:], m[:])
rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask))) rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
} }
func unblocksig(sig int32) {
var mask sigset
mask[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
rtsigprocmask(_SIG_UNBLOCK, &mask, nil, int32(unsafe.Sizeof(mask)))
}
...@@ -170,6 +170,10 @@ func badsignal2() { ...@@ -170,6 +170,10 @@ func badsignal2() {
var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n") var badsignal1 = []byte("runtime: signal received on thread not created by Go.\n")
func raisebadsignal(sig int32) {
badsignal2()
}
func madvise(addr unsafe.Pointer, n uintptr, flags int32) {} func madvise(addr unsafe.Pointer, n uintptr, flags int32) {}
func munmap(addr unsafe.Pointer, n uintptr) {} func munmap(addr unsafe.Pointer, n uintptr) {}
func resetcpuprofiler(hz int32) {} func resetcpuprofiler(hz int32) {}
......
...@@ -230,3 +230,9 @@ func updatesigmask(m sigmask) { ...@@ -230,3 +230,9 @@ func updatesigmask(m sigmask) {
copy(mask.__bits[:], m[:]) copy(mask.__bits[:], m[:])
sigprocmask(_SIG_SETMASK, &mask, nil) sigprocmask(_SIG_SETMASK, &mask, nil)
} }
func unblocksig(sig int32) {
var mask sigset
mask.__bits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
sigprocmask(_SIG_UNBLOCK, &mask, nil)
}
...@@ -239,3 +239,8 @@ func signalstack(s *stack) { ...@@ -239,3 +239,8 @@ func signalstack(s *stack) {
func updatesigmask(m sigmask) { func updatesigmask(m sigmask) {
sigprocmask(_SIG_SETMASK, m[0]) sigprocmask(_SIG_SETMASK, m[0])
} }
func unblocksig(sig int32) {
mask := uint32(1) << (uint32(sig) - 1)
sigprocmask(_SIG_UNBLOCK, mask)
}
...@@ -254,6 +254,10 @@ func badsignal2() { ...@@ -254,6 +254,10 @@ func badsignal2() {
exits(&_badsignal[0]) exits(&_badsignal[0])
} }
func raisebadsignal(sig int32) {
badsignal2()
}
func _atoi(b []byte) int { func _atoi(b []byte) int {
n := 0 n := 0
for len(b) > 0 && '0' <= b[0] && b[0] <= '9' { for len(b) > 0 && '0' <= b[0] && b[0] <= '9' {
......
...@@ -9,4 +9,7 @@ const ( ...@@ -9,4 +9,7 @@ const (
_SI_USER = 0x10001 _SI_USER = 0x10001
_SS_DISABLE = 4 _SS_DISABLE = 4
_RLIMIT_AS = 10 _RLIMIT_AS = 10
_SIG_BLOCK = 1
_SIG_UNBLOCK = 2
_SIG_SETMASK = 3
) )
...@@ -9,4 +9,7 @@ const ( ...@@ -9,4 +9,7 @@ const (
_NSIG = 33 _NSIG = 33
_SI_USER = 0x10001 _SI_USER = 0x10001
_RLIMIT_AS = 10 _RLIMIT_AS = 10
_SIG_BLOCK = 1
_SIG_UNBLOCK = 2
_SIG_SETMASK = 3
) )
...@@ -8,6 +8,8 @@ const ( ...@@ -8,6 +8,8 @@ const (
_SS_DISABLE = 2 _SS_DISABLE = 2
_NSIG = 65 _NSIG = 65
_SI_USER = 0 _SI_USER = 0
_SIG_BLOCK = 0
_SIG_UNBLOCK = 1
_SIG_SETMASK = 2 _SIG_SETMASK = 2
_RLIMIT_AS = 9 _RLIMIT_AS = 9
) )
......
...@@ -6,6 +6,7 @@ package runtime ...@@ -6,6 +6,7 @@ package runtime
const ( const (
_SS_DISABLE = 2 _SS_DISABLE = 2
_SIG_UNBLOCK = 2
_SIG_SETMASK = 3 _SIG_SETMASK = 3
_NSIG = 73 /* number of signals in sigtable array */ _NSIG = 73 /* number of signals in sigtable array */
_SI_USER = 0 _SI_USER = 0
......
...@@ -304,6 +304,12 @@ func updatesigmask(m sigmask) { ...@@ -304,6 +304,12 @@ func updatesigmask(m sigmask) {
sigprocmask(_SIG_SETMASK, &mask, nil) sigprocmask(_SIG_SETMASK, &mask, nil)
} }
func unblocksig(sig int32) {
var mask sigset
mask.__sigbits[(sig-1)/32] |= 1 << ((uint32(sig) - 1) & 31)
sigprocmask(_SIG_UNBLOCK, &mask, nil)
}
//go:nosplit //go:nosplit
func semacreate() uintptr { func semacreate() uintptr {
var sem *semt var sem *semt
......
...@@ -20,7 +20,7 @@ func mach_thread_self() uint32 ...@@ -20,7 +20,7 @@ func mach_thread_self() uint32
func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32 func sysctl(mib *uint32, miblen uint32, out *byte, size *uintptr, dst *byte, ndst uintptr) int32
//go:noescape //go:noescape
func sigprocmask(sig uint32, new, old *uint32) func sigprocmask(how uint32, new, old *uint32)
//go:noescape //go:noescape
func sigaction(mode uint32, new, old *sigactiont) func sigaction(mode uint32, new, old *sigactiont)
......
...@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) ...@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
func sigaction(sig int32, new, old *sigactiont) func sigaction(sig int32, new, old *sigactiont)
//go:noescape //go:noescape
func sigprocmask(new, old *sigset) func sigprocmask(how int32, new, old *sigset)
//go:noescape //go:noescape
func setitimer(mode int32, new, old *itimerval) func setitimer(mode int32, new, old *itimerval)
......
...@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer) ...@@ -19,7 +19,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
func sigaction(sig int32, new, old *sigactiont) func sigaction(sig int32, new, old *sigactiont)
//go:noescape //go:noescape
func sigprocmask(new, old *sigset) func sigprocmask(how int32, new, old *sigset)
//go:noescape //go:noescape
func setitimer(mode int32, new, old *itimerval) func setitimer(mode int32, new, old *itimerval)
......
...@@ -29,8 +29,8 @@ func rtsigprocmask(sig uint32, new, old *sigset, size int32) ...@@ -29,8 +29,8 @@ func rtsigprocmask(sig uint32, new, old *sigset, size int32)
//go:noescape //go:noescape
func getrlimit(kind int32, limit unsafe.Pointer) int32 func getrlimit(kind int32, limit unsafe.Pointer) int32
func raise(sig uint32) func raise(sig int32)
func raiseproc(sig uint32) func raiseproc(sig int32)
//go:noescape //go:noescape
func sched_getaffinity(pid, len uintptr, buf *uintptr) int32 func sched_getaffinity(pid, len uintptr, buf *uintptr) int32
......
...@@ -140,6 +140,43 @@ func sigpipe() { ...@@ -140,6 +140,43 @@ func sigpipe() {
raise(_SIGPIPE) raise(_SIGPIPE)
} }
// raisebadsignal is called when a signal is received on a non-Go
// thread, and the Go program does not want to handle it (that is, the
// program has not called os/signal.Notify for the signal).
func raisebadsignal(sig int32) {
if sig == _SIGPROF {
// Ignore profiling signals that arrive on non-Go threads.
return
}
var handler uintptr
if sig >= _NSIG {
handler = _SIG_DFL
} else {
handler = fwdSig[sig]
}
// Reset the signal handler and raise the signal.
// We are currently running inside a signal handler, so the
// signal is blocked. We need to unblock it before raising the
// signal, or the signal we raise will be ignored until we return
// from the signal handler. We know that the signal was unblocked
// before entering the handler, or else we would not have received
// it. That means that we don't have to worry about blocking it
// again.
unblocksig(sig)
setsig(sig, handler, false)
raise(sig)
// If the signal didn't cause the program to exit, restore the
// Go signal handler and carry on.
//
// We may receive another instance of the signal before we
// restore the Go handler, but that is not so bad: we know
// that the Go program has been ignoring the signal.
setsig(sig, funcPC(sighandler), true)
}
func crash() { func crash() {
if GOOS == "darwin" { if GOOS == "darwin" {
// OS X core dumps are linear dumps of the mapped memory, // OS X core dumps are linear dumps of the mapped memory,
......
...@@ -203,6 +203,12 @@ func sigdisable(sig uint32) { ...@@ -203,6 +203,12 @@ func sigdisable(sig uint32) {
func sigignore(sig uint32) { func sigignore(sig uint32) {
} }
func badsignal2()
func raisebadsignal(sig int32) {
badsignal2()
}
func crash() { func crash() {
// TODO: This routine should do whatever is needed // TODO: This routine should do whatever is needed
// to make the Windows program abort/crash as it // to make the Windows program abort/crash as it
......
...@@ -165,5 +165,13 @@ func signal_ignore(s uint32) { ...@@ -165,5 +165,13 @@ func signal_ignore(s uint32) {
// This runs on a foreign stack, without an m or a g. No stack split. // This runs on a foreign stack, without an m or a g. No stack split.
//go:nosplit //go:nosplit
func badsignal(sig uintptr) { func badsignal(sig uintptr) {
cgocallback(unsafe.Pointer(funcPC(sigsend)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig)) cgocallback(unsafe.Pointer(funcPC(badsignalgo)), noescape(unsafe.Pointer(&sig)), unsafe.Sizeof(sig))
}
func badsignalgo(sig uintptr) {
if !sigsend(uint32(sig)) {
// A foreign thread received the signal sig, and the
// Go code does not want to handle it.
raisebadsignal(int32(sig))
}
} }
...@@ -307,9 +307,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4 ...@@ -307,9 +307,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
RET RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $3, DI // arg 1 - how (SIG_SETMASK) MOVL how+0(FP), DI // arg 1 - how
MOVQ new+0(FP), SI // arg 2 - set MOVQ new+8(FP), SI // arg 2 - set
MOVQ old+8(FP), DX // arg 3 - oset MOVQ old+16(FP), DX // arg 3 - oset
MOVL $340, AX // sys_sigprocmask MOVL $340, AX // sys_sigprocmask
SYSCALL SYSCALL
JAE 2(PC) JAE 2(PC)
......
...@@ -355,10 +355,11 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4 ...@@ -355,10 +355,11 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
TEXT runtime·sigprocmask(SB),NOSPLIT,$16 TEXT runtime·sigprocmask(SB),NOSPLIT,$16
MOVL $0, 0(SP) // syscall gap MOVL $0, 0(SP) // syscall gap
MOVL $3, 4(SP) // arg 1 - how (SIG_SETMASK) MOVL how+0(FP), AX // arg 1 - how
MOVL new+0(FP), AX MOVL AX, 4(SP)
MOVL new+4(FP), AX
MOVL AX, 8(SP) // arg 2 - set MOVL AX, 8(SP) // arg 2 - set
MOVL old+4(FP), AX MOVL old+8(FP), AX
MOVL AX, 12(SP) // arg 3 - oset MOVL AX, 12(SP) // arg 3 - oset
MOVL $340, AX // sys_sigprocmask MOVL $340, AX // sys_sigprocmask
INT $0x80 INT $0x80
......
...@@ -295,9 +295,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4 ...@@ -295,9 +295,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
RET RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVL $3, DI // arg 1 - how (SIG_SETMASK) MOVL how+0(FP), DI // arg 1 - how
MOVQ new+0(FP), SI // arg 2 - set MOVQ new+8(FP), SI // arg 2 - set
MOVQ old+8(FP), DX // arg 3 - oset MOVQ old+16(FP), DX // arg 3 - oset
MOVL $340, AX // sys_sigprocmask MOVL $340, AX // sys_sigprocmask
SYSCALL SYSCALL
JAE 2(PC) JAE 2(PC)
......
...@@ -327,9 +327,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4 ...@@ -327,9 +327,9 @@ TEXT runtime·osyield(SB),NOSPLIT,$-4
RET RET
TEXT runtime·sigprocmask(SB),NOSPLIT,$0 TEXT runtime·sigprocmask(SB),NOSPLIT,$0
MOVW $3, R0 // arg 1 - how (SIG_SETMASK) MOVW how+0(FP), R0 // arg 1 - how
MOVW new+0(FP), R1 // arg 2 - set MOVW new+4(FP), R1 // arg 2 - set
MOVW old+4(FP), R2 // arg 3 - oset MOVW old+8(FP), R2 // arg 3 - oset
MOVW $SYS_sigprocmask, R7 MOVW $SYS_sigprocmask, R7
SWI $0 SWI $0
MOVW.CS $0, R8 // crash on syscall failure MOVW.CS $0, R8 // crash on syscall failure
......
...@@ -219,7 +219,7 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36 ...@@ -219,7 +219,7 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
RET RET
TEXT runtime·sigfwd(SB),NOSPLIT,$0-32 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
MOVQ sig+8(FP), DI MOVL sig+8(FP), DI
MOVQ info+16(FP), SI MOVQ info+16(FP), SI
MOVQ ctx+24(FP), DX MOVQ ctx+24(FP), DX
MOVQ fn+0(FP), AX MOVQ fn+0(FP), AX
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment