]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: factor out windows sigtramp
authorqmuntal <quimmuntal@gmail.com>
Fri, 16 Dec 2022 15:54:03 +0000 (16:54 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Tue, 24 Jan 2023 12:05:07 +0000 (12:05 +0000)
This CL factors out part of the Windows sigtramp implementation, which
was duplicated in all four architectures. The new common code is
implemented in Go rather than in assembly, which will make Windows
error handling easier to reason and maintain.

While here, implement the control flow guard workaround on
windows/386, which almost comes for free.

Change-Id: I0bf38c28c54793225126e161bd95527a62de05e0
Reviewed-on: https://go-review.googlesource.com/c/go/+/458135
Run-TryBot: Quim Muntal <quimmuntal@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
src/runtime/defs_windows.go
src/runtime/defs_windows_386.go
src/runtime/defs_windows_amd64.go
src/runtime/defs_windows_arm.go
src/runtime/defs_windows_arm64.go
src/runtime/signal_windows.go
src/runtime/sys_windows_386.s
src/runtime/sys_windows_amd64.s
src/runtime/sys_windows_arm.s
src/runtime/sys_windows_arm64.s

index 8d4e38120ea538f47588a0dac49d6648d07a11bc..7e5d9570f80e64e62dcfcd17a8cf6ffeabafce4d 100644 (file)
@@ -57,6 +57,11 @@ type systeminfo struct {
        wprocessorrevision          uint16
 }
 
+type exceptionpointers struct {
+       record  *exceptionrecord
+       context *context
+}
+
 type exceptionrecord struct {
        exceptioncode        uint32
        exceptionflags       uint32
index 37fe74c542364fa60355672854cd29b9c724821d..8d6c443a14f3fef8940be0c580f53d6c59200b26 100644 (file)
@@ -56,6 +56,11 @@ func (c *context) set_lr(x uintptr) {}
 func (c *context) set_ip(x uintptr) { c.eip = uint32(x) }
 func (c *context) set_sp(x uintptr) { c.esp = uint32(x) }
 
+func prepareContextForSigResume(c *context) {
+       c.edx = c.esp
+       c.ecx = c.eip
+}
+
 func dumpregs(r *context) {
        print("eax     ", hex(r.eax), "\n")
        print("ebx     ", hex(r.ebx), "\n")
index ac636a68ecc57ee81d6b575df8124eb6b7e995e6..afa8a657b886627f765c8b4416eec9f01fb6828d 100644 (file)
@@ -70,6 +70,11 @@ func (c *context) set_lr(x uintptr) {}
 func (c *context) set_ip(x uintptr) { c.rip = uint64(x) }
 func (c *context) set_sp(x uintptr) { c.rsp = uint64(x) }
 
+func prepareContextForSigResume(c *context) {
+       c.r8 = c.rsp
+       c.r9 = c.rip
+}
+
 func dumpregs(r *context) {
        print("rax     ", hex(r.rax), "\n")
        print("rbx     ", hex(r.rbx), "\n")
index 370470e35d94fb9846b659d9fd093b79dbd1e071..21c7991519b0ed31a0c3115672c6021a36e8add0 100644 (file)
@@ -58,6 +58,11 @@ func (c *context) set_ip(x uintptr) { c.pc = uint32(x) }
 func (c *context) set_sp(x uintptr) { c.spr = uint32(x) }
 func (c *context) set_lr(x uintptr) { c.lrr = uint32(x) }
 
+func prepareContextForSigResume(c *context) {
+       c.r0 = c.spr
+       c.r1 = c.pc
+}
+
 func dumpregs(r *context) {
        print("r0   ", hex(r.r0), "\n")
        print("r1   ", hex(r.r1), "\n")
index 9ccce46f096b9a216807460c9421fdede08b1547..6c71133b43e8255ea1fe7dc778cf63557157f873 100644 (file)
@@ -41,6 +41,11 @@ func (c *context) set_ip(x uintptr) { c.pc = uint64(x) }
 func (c *context) set_sp(x uintptr) { c.xsp = uint64(x) }
 func (c *context) set_lr(x uintptr) { c.x[30] = uint64(x) }
 
+func prepareContextForSigResume(c *context) {
+       c.x[0] = c.xsp
+       c.x[1] = c.pc
+}
+
 func dumpregs(r *context) {
        print("r0   ", hex(r.x[0]), "\n")
        print("r1   ", hex(r.x[1]), "\n")
index 37986cd6b5c3bae0e0fd433faa0acf40e3a012f9..0686be4635adc6354e6269e981d8cdaf5a53996c 100644 (file)
@@ -22,10 +22,11 @@ func disableWER() {
        stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
 }
 
-// in sys_windows_386.s and sys_windows_amd64.s
+// in sys_windows_386.s, sys_windows_amd64.s, sys_windows_arm.s, and sys_windows_arm64.s
 func exceptiontramp()
 func firstcontinuetramp()
 func lastcontinuetramp()
+func sigresume()
 
 func initExceptionHandler() {
        stdcall2(_AddVectoredExceptionHandler, 1, abi.FuncPCABI0(exceptiontramp))
@@ -88,13 +89,105 @@ func isgoexception(info *exceptionrecord, r *context) bool {
        return true
 }
 
+const (
+       callbackVEH = iota
+       callbackFirstVCH
+       callbackLastVCH
+)
+
+// sigFetchGSafe is like getg() but without panicking
+// when TLS is not set.
+// Only implemented on windows/386, which is the only
+// arch that loads TLS when calling getg(). Others
+// use a dedicated register.
+func sigFetchGSafe() *g
+
+func sigFetchG() *g {
+       if GOARCH == "386" {
+               return sigFetchGSafe()
+       }
+       return getg()
+}
+
+// sigtrampgo is called from the exception handler function, sigtramp,
+// written in assembly code.
+// Return EXCEPTION_CONTINUE_EXECUTION if the exception is handled,
+// else return EXCEPTION_CONTINUE_SEARCH.
+//
+// It is nosplit for the same reason as exceptionhandler.
+//
+//go:nosplit
+func sigtrampgo(ep *exceptionpointers, kind int) int32 {
+       gp := sigFetchG()
+       if gp == nil {
+               return _EXCEPTION_CONTINUE_SEARCH
+       }
+
+       var fn func(info *exceptionrecord, r *context, gp *g) int32
+       switch kind {
+       case callbackVEH:
+               fn = exceptionhandler
+       case callbackFirstVCH:
+               fn = firstcontinuehandler
+       case callbackLastVCH:
+               fn = lastcontinuehandler
+       default:
+               throw("unknown sigtramp callback")
+       }
+
+       // Check if we are running on g0 stack, and if we are,
+       // call fn directly instead of creating the closure.
+       // for the systemstack argument.
+       //
+       // A closure can't be marked as nosplit, so it might
+       // call morestack if we are at the g0 stack limit.
+       // If that happens, the runtime will call abort
+       // and end up in sigtrampgo again.
+       // TODO: revisit this workaround if/when closures
+       // can be compiled as nosplit.
+       //
+       // Note that this scenario should only occur on
+       // TestG0StackOverflow. Any other occurrence should
+       // be treated as a bug.
+       var ret int32
+       if gp != gp.m.g0 {
+               systemstack(func() {
+                       ret = fn(ep.record, ep.context, gp)
+               })
+       } else {
+               ret = fn(ep.record, ep.context, gp)
+       }
+       if ret == _EXCEPTION_CONTINUE_SEARCH {
+               return ret
+       }
+
+       // Check if we need to set up the control flow guard workaround.
+       // On Windows, the stack pointer in the context must lie within
+       // system stack limits when we resume from exception.
+       // Store the resume SP and PC in alternate registers
+       // and return to sigresume on the g0 stack.
+       // sigresume makes no use of the stack at all,
+       // loading SP from RX and jumping to RY, being RX and RY two scratch registers.
+       // Note that blindly smashing RX and RY is only safe because we know sigpanic
+       // will not actually return to the original frame, so the registers
+       // are effectively dead. But this does mean we can't use the
+       // same mechanism for async preemption.
+       if ep.context.ip() == abi.FuncPCABI0(sigresume) {
+               // sigresume has already been set up by a previous exception.
+               return ret
+       }
+       prepareContextForSigResume(ep.context)
+       ep.context.set_sp(gp.m.g0.sched.sp)
+       ep.context.set_ip(abi.FuncPCABI0(sigresume))
+       return ret
+}
+
 // Called by sigtramp from Windows VEH handler.
 // Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
 // or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
 //
-// This is the first entry into Go code for exception handling. This
-// is nosplit to avoid growing the stack until we've checked for
-// _EXCEPTION_BREAKPOINT, which is raised if we overflow the g0 stack,
+// This is nosplit to avoid growing the stack until we've checked for
+// _EXCEPTION_BREAKPOINT, which is raised by abort() if we overflow the g0 stack.
 //
 //go:nosplit
 func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
index 8713f7d0d9c6482ab9ca3937f2f54aa29437d746..0983cc7b1fdf6b7a33601f31b6c0a72fedc96740 100644 (file)
@@ -72,13 +72,20 @@ TEXT runtime·getlasterror(SB),NOSPLIT,$0
        MOVL    AX, ret+0(FP)
        RET
 
+TEXT runtime·sigFetchGSafe<ABIInternal>(SB),NOSPLIT|NOFRAME,$0
+       get_tls(AX)
+       CMPL    AX, $0
+       JE      2(PC)
+       MOVL    g(AX), AX
+       MOVL    AX, ret+0(FP)
+       RET
+
 // Called by Windows as a Vectored Exception Handler (VEH).
-// First argument is pointer to struct containing
+// AX is pointer to struct containing
 // exception record and context pointers.
-// Handler function is stored in AX.
-// Return 0 for 'not handled', -1 for handled.
+// CX is the kind of sigtramp function.
+// Return value of sigtrampgo is stored in AX.
 TEXT sigtramp<>(SB),NOSPLIT,$0-0
-       MOVL    ptrs+0(FP), CX
        SUBL    $40, SP
 
        // save callee-saved registers
@@ -87,58 +94,11 @@ TEXT sigtramp<>(SB),NOSPLIT,$0-0
        MOVL    SI, 20(SP)
        MOVL    DI, 24(SP)
 
-       MOVL    AX, SI  // save handler address
-
-       // find g
-       get_tls(DX)
-       CMPL    DX, $0
-       JNE     3(PC)
-       MOVL    $0, AX // continue
-       JMP     done
-       MOVL    g(DX), DX
-       CMPL    DX, $0
-       JNE     2(PC)
-       CALL    runtime·badsignal2(SB)
-
-       // save g in case of stack switch
-       MOVL    DX, 32(SP)      // g
-       MOVL    SP, 36(SP)
-
-       // do we need to switch to the g0 stack?
-       MOVL    g_m(DX), BX
-       MOVL    m_g0(BX), BX
-       CMPL    DX, BX
-       JEQ     g0
-
-       // switch to the g0 stack
-       get_tls(BP)
-       MOVL    BX, g(BP)
-       MOVL    (g_sched+gobuf_sp)(BX), DI
-       // make room for sighandler arguments
-       // and re-save old SP for restoring later.
-       // (note that the 36(DI) here must match the 36(SP) above.)
-       SUBL    $40, DI
-       MOVL    SP, 36(DI)
-       MOVL    DI, SP
-
-g0:
-       MOVL    0(CX), BX // ExceptionRecord*
-       MOVL    4(CX), CX // Context*
-       MOVL    BX, 0(SP)
+       MOVL    AX, 0(SP)
        MOVL    CX, 4(SP)
-       MOVL    DX, 8(SP)
-       CALL    SI      // call handler
-       // AX is set to report result back to Windows
-       MOVL    12(SP), AX
-
-       // switch back to original stack and g
-       // no-op if we never left.
-       MOVL    36(SP), SP
-       MOVL    32(SP), DX      // note: different SP
-       get_tls(BP)
-       MOVL    DX, g(BP)
-
-done:
+       CALL    runtime·sigtrampgo(SB)
+       MOVL    8(SP), AX
+
        // restore callee-saved registers
        MOVL    24(SP), DI
        MOVL    20(SP), SI
@@ -150,8 +110,18 @@ done:
        BYTE $0xC2; WORD $4
        RET // unreached; make assembler happy
 
+// Trampoline to resume execution from exception handler.
+// This is part of the control flow guard workaround.
+// It switches stacks and jumps to the continuation address.
+// DX and CX are set above at the end of sigtrampgo
+// in the context that starts executing at sigresume.
+TEXT runtime·sigresume(SB),NOSPLIT|NOFRAME,$0
+       MOVL    DX, SP
+       JMP     CX
+
 TEXT runtime·exceptiontramp(SB),NOSPLIT,$0
-       MOVL    $runtime·exceptionhandler(SB), AX
+       MOVL    argframe+0(FP), AX
+       MOVL    $const_callbackVEH, CX
        JMP     sigtramp<>(SB)
 
 TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
@@ -159,7 +129,8 @@ TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
        INT     $3
 
 TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0
-       MOVL    $runtime·lastcontinuehandler(SB), AX
+       MOVL    argframe+0(FP), AX
+       MOVL    $const_callbackLastVCH, CX
        JMP     sigtramp<>(SB)
 
 GLOBL runtime·cbctxts(SB), NOPTR, $4
index 777726f7c11fc57b5a726fff7341ad72127f699c..04e649539c7fef5be6784bd087a5d321e0c04776 100644 (file)
@@ -107,132 +107,61 @@ TEXT runtime·getlasterror(SB),NOSPLIT,$0
        RET
 
 // Called by Windows as a Vectored Exception Handler (VEH).
-// First argument is pointer to struct containing
+// CX is pointer to struct containing
 // exception record and context pointers.
-// Handler function is stored in AX.
-// Return 0 for 'not handled', -1 for handled.
+// DX is the kind of sigtramp function.
+// Return value of sigtrampgo is stored in AX.
 TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0-0
-       // CX: PEXCEPTION_POINTERS ExceptionInfo
-
        // Switch from the host ABI to the Go ABI.
        PUSH_REGS_HOST_TO_ABI0()
-       // Make stack space for the rest of the function.
-       ADJSP   $48
-
-       MOVQ    CX, R13 // save exception address
-       MOVQ    AX, R15 // save handler address
-
-       // find g
-       get_tls(DX)
-       CMPQ    DX, $0
-       JNE     3(PC)
-       MOVQ    $0, AX // continue
-       JMP     done
-       MOVQ    g(DX), DX
-       CMPQ    DX, $0
-       JNE     2(PC)
-       CALL    runtime·badsignal2(SB)
-
-       // save g and SP in case of stack switch
-       MOVQ    DX, 32(SP) // g
-       MOVQ    SP, 40(SP)
-
-       // do we need to switch to the g0 stack?
-       MOVQ    g_m(DX), BX
-       MOVQ    m_g0(BX), BX
-       CMPQ    DX, BX
-       JEQ     g0
-
-       // switch to g0 stack
-       get_tls(BP)
-       MOVQ    BX, g(BP)
-       MOVQ    (g_sched+gobuf_sp)(BX), DI
-       // make room for sighandler arguments
-       // and re-save old SP for restoring later.
-       // Adjust g0 stack by the space we're using and
-       // save SP at the same place on the g0 stack.
-       // The 40(DI) here must match the 40(SP) above.
-       SUBQ    $(REGS_HOST_TO_ABI0_STACK + 48), DI
-       MOVQ    SP, 40(DI)
-       MOVQ    DI, SP
-
-g0:
-       MOVQ    0(R13), BX // ExceptionRecord*
-       MOVQ    8(R13), CX // Context*
-       MOVQ    BX, 0(SP)
-       MOVQ    CX, 8(SP)
-       MOVQ    DX, 16(SP)
-       CALL    R15     // call handler
-       // AX is set to report result back to Windows
-       MOVL    24(SP), AX
-
-       MOVQ    SP, DI // save g0 SP
-
-       // switch back to original stack and g
-       // no-op if we never left.
-       MOVQ    40(SP), SP
-       MOVQ    32(SP), DX
-       get_tls(BP)
-       MOVQ    DX, g(BP)
 
-       // if return value is CONTINUE_SEARCH, do not set up control
-       // flow guard workaround.
+       // Set up ABIInternal environment: cleared X15 and R14.
+       // R14 is cleared in case there's a non-zero value in there
+       // if called from a non-go thread.
+       XORPS   X15, X15
+       XORQ    R14, R14
+
+       get_tls(AX)
        CMPQ    AX, $0
-       JEQ     done
-
-       // Check if we need to set up the control flow guard workaround.
-       // On Windows, the stack pointer in the context must lie within
-       // system stack limits when we resume from exception.
-       // Store the resume SP and PC in alternate registers
-       // and return to sigresume on the g0 stack.
-       // sigresume makes no use of the stack at all,
-       // loading SP from R8 and jumping to R9.
-       // Note that smashing R8 and R9 is only safe because we know sigpanic
-       // will not actually return to the original frame, so the registers
-       // are effectively dead. But this does mean we can't use the
-       // same mechanism for async preemption.
-       MOVQ    8(R13), CX
-       MOVQ    $sigresume<>(SB), BX
-       CMPQ    BX, context_rip(CX)
-       JEQ     done                    // do not clobber saved SP/PC
-
-       // Save resume SP and PC into R8, R9.
-       MOVQ    context_rsp(CX), BX
-       MOVQ    BX, context_r8(CX)
-       MOVQ    context_rip(CX), BX
-       MOVQ    BX, context_r9(CX)
-
-       // Set up context record to return to sigresume on g0 stack
-       MOVD    DI, BX
-       MOVD    BX, context_rsp(CX)
-       MOVD    $sigresume<>(SB), BX
-       MOVD    BX, context_rip(CX)
-
-done:
-       ADJSP   $-48
-       POP_REGS_HOST_TO_ABI0()
+       JE      2(PC)
+       // Exception from Go thread, set R14.
+       MOVQ    g(AX), R14
 
+       // Reserve space for spill slots.
+       ADJSP   $16
+       MOVQ    CX, AX
+       MOVQ    DX, BX
+       // Calling ABIInternal because TLS might be nil.
+       CALL    runtime·sigtrampgo<ABIInternal>(SB)
+       // Return value is already stored in AX.
+
+       ADJSP   $-16
+
+       POP_REGS_HOST_TO_ABI0()
        RET
 
 // Trampoline to resume execution from exception handler.
 // This is part of the control flow guard workaround.
 // It switches stacks and jumps to the continuation address.
-// R8 and R9 are set above at the end of sigtramp<>
-// in the context that starts executing at sigresume<>.
-TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0
+// R8 and R9 are set above at the end of sigtrampgo
+// in the context that starts executing at sigresume.
+TEXT runtime·sigresume(SB),NOSPLIT|NOFRAME,$0
        MOVQ    R8, SP
        JMP     R9
 
 TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0
-       MOVQ    $runtime·exceptionhandler(SB), AX
+       // PExceptionPointers already on CX
+       MOVQ    $const_callbackVEH, DX
        JMP     sigtramp<>(SB)
 
 TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0
-       MOVQ    $runtime·firstcontinuehandler(SB), AX
+       // PExceptionPointers already on CX
+       MOVQ    $const_callbackFirstVCH, DX
        JMP     sigtramp<>(SB)
 
 TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0
-       MOVQ    $runtime·lastcontinuehandler(SB), AX
+       // PExceptionPointers already on CX
+       MOVQ    $const_callbackLastVCH, DX
        JMP     sigtramp<>(SB)
 
 GLOBL runtime·cbctxts(SB), NOPTR, $8
index db6d8f1a08741d2be22911baf770c68932c65bec..a00fd166705b14e50f016c2eadcb8cc5e4ce4a58 100644 (file)
@@ -107,121 +107,36 @@ TEXT runtime·getlasterror(SB),NOSPLIT,$0
        RET
 
 // Called by Windows as a Vectored Exception Handler (VEH).
-// First argument is pointer to struct containing
+// R0 is pointer to struct containing
 // exception record and context pointers.
-// Handler function is stored in R1
-// Return 0 for 'not handled', -1 for handled.
-// int32_t sigtramp(
-//     PEXCEPTION_POINTERS ExceptionInfo,
-//     func *GoExceptionHandler);
+// R1 is the kind of sigtramp function.
+// Return value of sigtrampgo is stored in R0.
 TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0
-       MOVM.DB.W [R0, R4-R11, R14], (R13)      // push {r0, r4-r11, lr} (SP-=40)
-       SUB     $(8+20), R13            // reserve space for g, sp, and
-                                       // parameters/retval to go call
+       MOVM.DB.W [R4-R11, R14], (R13)  // push {r4-r11, lr} (SP-=40)
+       SUB     $(16), R13              // reserve space for parameters/retval to go call
 
        MOVW    R0, R6                  // Save param0
        MOVW    R1, R7                  // Save param1
-
-       BL      runtime·load_g(SB)
-       CMP     $0,     g               // is there a current g?
-       BNE     g_ok
-       ADD     $(8+20), R13    // free locals
-       MOVM.IA.W (R13), [R3, R4-R11, R14]      // pop {r3, r4-r11, lr}
-       MOVW    $0, R0          // continue 
-       BEQ     return
-
-g_ok:
-
-       // save g and SP in case of stack switch
-       MOVW    R13, 24(R13)
-       MOVW    g, 20(R13)
-
-       // do we need to switch to the g0 stack?
-       MOVW    g, R5                   // R5 = g
-       MOVW    g_m(R5), R2             // R2 = m
-       MOVW    m_g0(R2), R4            // R4 = g0
-       CMP     R5, R4                  // if curg == g0
-       BEQ     g0
-
-       // switch to g0 stack
-       MOVW    R4, g                           // g = g0
-       MOVW    (g_sched+gobuf_sp)(g), R3       // R3 = g->gobuf.sp
-       BL      runtime·save_g(SB)
-
-       // make room for sighandler arguments
-       // and re-save old SP for restoring later.
-       // (note that the 24(R3) here must match the 24(R13) above.)
-       SUB     $40, R3
-       MOVW    R13, 24(R3)             // save old stack pointer
-       MOVW    R3, R13                 // switch stack
-
-g0:
-       MOVW    0(R6), R2       // R2 = ExceptionPointers->ExceptionRecord
-       MOVW    4(R6), R3       // R3 = ExceptionPointers->ContextRecord
+       BL      runtime·load_g(SB)     // Clobbers R0
 
        MOVW    $0, R4
        MOVW    R4, 0(R13)      // No saved link register.
-       MOVW    R2, 4(R13)      // Move arg0 (ExceptionRecord) into position
-       MOVW    R3, 8(R13)      // Move arg1 (ContextRecord) into position
-       MOVW    R5, 12(R13)     // Move arg2 (original g) into position
-       BL      (R7)            // Call the goroutine
-       MOVW    16(R13), R4     // Fetch return value from stack
-
-       // Save system stack pointer for sigresume setup below.
-       // The exact value does not matter - nothing is read or written
-       // from this address. It just needs to be on the system stack.
-       MOVW    R13, R12
-
-       // switch back to original stack and g
-       MOVW    24(R13), R13
-       MOVW    20(R13), g
-       BL      runtime·save_g(SB)
-
-done:
-       MOVW    R4, R0                          // move retval into position
-       ADD     $(8 + 20), R13                  // free locals
-       MOVM.IA.W (R13), [R3, R4-R11, R14]      // pop {r3, r4-r11, lr}
-
-       // if return value is CONTINUE_SEARCH, do not set up control
-       // flow guard workaround
-       CMP     $0, R0
-       BEQ     return
-
-       // Check if we need to set up the control flow guard workaround.
-       // On Windows, the stack pointer in the context must lie within
-       // system stack limits when we resume from exception.
-       // Store the resume SP and PC on the g0 stack,
-       // and return to sigresume on the g0 stack. sigresume
-       // pops the saved PC and SP from the g0 stack, resuming execution
-       // at the desired location.
-       // If sigresume has already been set up by a previous exception
-       // handler, don't clobber the stored SP and PC on the stack.
-       MOVW    4(R3), R3                       // PEXCEPTION_POINTERS->Context
-       MOVW    context_pc(R3), R2              // load PC from context record
-       MOVW    $sigresume<>(SB), R1
-       CMP     R1, R2
-       B.EQ    return                          // do not clobber saved SP/PC
-
-       // Save resume SP and PC into R0, R1.
-       MOVW    context_spr(R3), R2
-       MOVW    R2, context_r0(R3)
-       MOVW    context_pc(R3), R2
-       MOVW    R2, context_r1(R3)
+       MOVW    R6, 4(R13)      // Move arg0 into position
+       MOVW    R7, 8(R13)      // Move arg1 into position
+       BL      runtime·sigtrampgo(SB)
+       MOVW    12(R13), R0     // Fetch return value from stack
 
-       // Set up context record to return to sigresume on g0 stack
-       MOVW    R12, context_spr(R3)
-       MOVW    $sigresume<>(SB), R2
-       MOVW    R2, context_pc(R3)
+       ADD     $(16), R13                      // free locals
+       MOVM.IA.W (R13), [R4-R11, R14]  // pop {r4-r11, lr}
 
-return:
        B       (R14)                           // return
 
 // Trampoline to resume execution from exception handler.
 // This is part of the control flow guard workaround.
 // It switches stacks and jumps to the continuation address.
-// R0 and R1 are set above at the end of sigtramp<>
-// in the context that starts executing at sigresume<>.
-TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0
+// R0 and R1 are set above at the end of sigtrampgo
+// in the context that starts executing at sigresume.
+TEXT runtime·sigresume(SB),NOSPLIT|NOFRAME,$0
        // Important: do not smash LR,
        // which is set to a live value when handling
        // a signal by pushing a call to sigpanic onto the stack.
@@ -229,15 +144,15 @@ TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0
        B       (R1)
 
 TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0
-       MOVW    $runtime·exceptionhandler(SB), R1
+       MOVW    $const_callbackVEH, R1
        B       sigtramp<>(SB)
 
 TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0
-       MOVW    $runtime·firstcontinuehandler(SB), R1
+       MOVW    $const_callbackFirstVCH, R1
        B       sigtramp<>(SB)
 
 TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0
-       MOVW    $runtime·lastcontinuehandler(SB), R1
+       MOVW    $const_callbackLastVCH, R1
        B       sigtramp<>(SB)
 
 GLOBL runtime·cbctxts(SB), NOPTR, $4
index 4702a4d7d254f742fc58fa12a1464a775e149ab7..67fe21b2d09487a6aa91fab7d20fc22e13843eda 100644 (file)
@@ -131,143 +131,38 @@ TEXT runtime·getlasterror(SB),NOSPLIT|NOFRAME,$0
        RET
 
 // Called by Windows as a Vectored Exception Handler (VEH).
-// First argument is pointer to struct containing
+// R0 is pointer to struct containing
 // exception record and context pointers.
-// Handler function is stored in R1
-// Return 0 for 'not handled', -1 for handled.
-// int32_t sigtramp(
-//     PEXCEPTION_POINTERS ExceptionInfo,
-//     func *GoExceptionHandler);
-TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0
-       // Save R0, R1 (args) as well as LR, R27, R28 (callee-save).
+// R1 is the kind of sigtramp function.
+// Return value of sigtrampgo is stored in R0.
+TEXT sigtramp<>(SB),NOSPLIT,$176
+       // Switch from the host ABI to the Go ABI, safe args and lr.
        MOVD    R0, R5
        MOVD    R1, R6
        MOVD    LR, R7
-       MOVD    R27, R16                // saved R27 (callee-save)
-       MOVD    g, R17                  // saved R28 (callee-save from Windows, not really g)
+       SAVE_R19_TO_R28(8*4)
+       SAVE_F8_TO_F15(8*14)
 
-       BL      runtime·load_g(SB)     // smashes R0, R27, R28 (g)
-       CMP     $0,     g               // is there a current g?
-       BNE     g_ok
-       MOVD    R7, LR
-       MOVD    R16, R27        // restore R27
-       MOVD    R17, g          // restore R28
-       MOVD    $0, R0          // continue 
-       RET
+       BL      runtime·load_g(SB)     // Clobers R0, R27, R28 (g)
 
-g_ok:
-       // Do we need to switch to the g0 stack?
-       MOVD    g, R3                   // R3 = oldg (for sigtramp_g0)
-       MOVD    g_m(g), R2              // R2 = m
-       MOVD    m_g0(R2), R2            // R2 = g0
-       CMP     g, R2                   // if curg == g0
-       BNE     switch
-
-       // No: on g0 stack already, tail call to sigtramp_g0.
-       // Restore all the callee-saves so sigtramp_g0 can return to our caller.
-       // We also pass R2 = g0, R3 = oldg, both set above.
        MOVD    R5, R0
        MOVD    R6, R1
-       MOVD    R7, LR
-       MOVD    R16, R27                // restore R27
-       MOVD    R17, g                  // restore R28
-       B       sigtramp_g0<>(SB)
+       // Calling ABIInternal because TLS might be nil.
+       BL      runtime·sigtrampgo<ABIInternal>(SB)
+       // Return value is already stored in R0.
 
-switch:
-       // switch to g0 stack (but do not update g - that's sigtramp_g0's job)
-       MOVD    RSP, R8
-       MOVD    (g_sched+gobuf_sp)(R2), R4      // R4 = g->gobuf.sp
-       SUB     $(6*8), R4                      // alloc space for saves - 2 words below SP for frame pointer, 3 for us to use, 1 for alignment
-       MOVD    R4, RSP                         // switch to g0 stack
-
-       MOVD    $0, (0*8)(RSP)  // fake saved LR
-       MOVD    R7, (1*8)(RSP)  // saved LR
-       MOVD    R8, (2*8)(RSP)  // saved SP
-
-       MOVD    R5, R0          // original args
-       MOVD    R6, R1          // original args
-       MOVD    R16, R27
-       MOVD    R17, g          // R28
-       BL      sigtramp_g0<>(SB)
-
-       // switch back to original stack; g already updated
-       MOVD    (1*8)(RSP), R7  // saved LR
-       MOVD    (2*8)(RSP), R8  // saved SP
+       // Restore callee-save registers.
+       RESTORE_R19_TO_R28(8*4)
+       RESTORE_F8_TO_F15(8*14)
        MOVD    R7, LR
-       MOVD    R8, RSP
-       RET
-
-// sigtramp_g0 is running on the g0 stack, with R2 = g0, R3 = oldg.
-// But g itself is not set - that's R28, a callee-save register,
-// and it still holds the value from the Windows DLL caller.
-TEXT sigtramp_g0<>(SB),NOSPLIT,$128
-       NO_LOCAL_POINTERS
-
-       // Push C callee-save registers R19-R28. LR, FP already saved.
-       // These registers will occupy the upper 10 words of the frame.
-       SAVE_R19_TO_R28(8*7)
-
-       MOVD    0(R0), R5       // R5 = ExceptionPointers->ExceptionRecord
-       MOVD    8(R0), R6       // R6 = ExceptionPointers->ContextRecord
-       MOVD    R6, context-(11*8)(SP)
-
-       MOVD    R2, g                   // g0
-       BL      runtime·save_g(SB)     // smashes R0
-
-       MOVD    R5, (1*8)(RSP)  // arg0 (ExceptionRecord)
-       MOVD    R6, (2*8)(RSP)  // arg1 (ContextRecord)
-       MOVD    R3, (3*8)(RSP)  // arg2 (original g)
-       MOVD    R3, oldg-(12*8)(SP)
-       BL      (R1)
-       MOVD    oldg-(12*8)(SP), g
-       BL      runtime·save_g(SB)     // smashes R0
-       MOVW    (4*8)(RSP), R0  // return value (0 or -1)
-
-       // if return value is CONTINUE_SEARCH, do not set up control
-       // flow guard workaround
-       CMP     $0, R0
-       BEQ     return
-
-       // Check if we need to set up the control flow guard workaround.
-       // On Windows, the stack pointer in the context must lie within
-       // system stack limits when we resume from exception.
-       // Store the resume SP and PC in alternate registers
-       // and return to sigresume on the g0 stack.
-       // sigresume makes no use of the stack at all,
-       // loading SP from R0 and jumping to R1.
-       // Note that smashing R0 and R1 is only safe because we know sigpanic
-       // will not actually return to the original frame, so the registers
-       // are effectively dead. But this does mean we can't use the
-       // same mechanism for async preemption.
-       MOVD    context-(11*8)(SP), R6
-       MOVD    context_pc(R6), R2              // load PC from context record
-       MOVD    $sigresume<>(SB), R1
-
-       CMP     R1, R2
-       BEQ     return                          // do not clobber saved SP/PC
-
-       // Save resume SP and PC into R0, R1.
-       MOVD    context_xsp(R6), R2
-       MOVD    R2, (context_x+0*8)(R6)
-       MOVD    context_pc(R6), R2
-       MOVD    R2, (context_x+1*8)(R6)
-
-       // Set up context record to return to sigresume on g0 stack
-       MOVD    RSP, R2
-       MOVD    R2, context_xsp(R6)
-       MOVD    $sigresume<>(SB), R2
-       MOVD    R2, context_pc(R6)
-
-return:
-       RESTORE_R19_TO_R28(8*7)         // smashes g
        RET
 
 // Trampoline to resume execution from exception handler.
 // This is part of the control flow guard workaround.
 // It switches stacks and jumps to the continuation address.
-// R0 and R1 are set above at the end of sigtramp<>
-// in the context that starts executing at sigresume<>.
-TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0
+// R0 and R1 are set above at the end of sigtrampgo
+// in the context that starts executing at sigresume.
+TEXT runtime·sigresume(SB),NOSPLIT|NOFRAME,$0
        // Important: do not smash LR,
        // which is set to a live value when handling
        // a signal by pushing a call to sigpanic onto the stack.
@@ -275,15 +170,15 @@ TEXT sigresume<>(SB),NOSPLIT|NOFRAME,$0
        B       (R1)
 
 TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0
-       MOVD    $runtime·exceptionhandler(SB), R1
+       MOVD    $const_callbackVEH, R1
        B       sigtramp<>(SB)
 
 TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0
-       MOVD    $runtime·firstcontinuehandler(SB), R1
+       MOVD    $const_callbackFirstVCH, R1
        B       sigtramp<>(SB)
 
 TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0
-       MOVD    $runtime·lastcontinuehandler(SB), R1
+       MOVD    $const_callbackLastVCH, R1
        B       sigtramp<>(SB)
 
 GLOBL runtime·cbctxts(SB), NOPTR, $4