]> Cypherpunks repositories - gostls13.git/commitdiff
runtime,runtime/cgo: save all necessary registers on entry to Go on Windows
authorAustin Clements <austin@google.com>
Tue, 13 Apr 2021 13:06:50 +0000 (09:06 -0400)
committerAustin Clements <austin@google.com>
Thu, 15 Apr 2021 12:38:11 +0000 (12:38 +0000)
There are several assembly functions that transition from the Windows
ABI to the Go ABI. These all need to save all registers that are
callee-save in the Windows ABI and caller-save in the Go ABI and
prepare the register state for Go. However, they all do this slightly
differently and most of them don't save the necessary XMM registers
for this transition (which could corrupt them in the C caller).
Furthermore, now that we have a carefully specified Go ABI, it's clear
that none of these actually get all of the details 100% right.

So, unify this code into two macros in a shared header in
runtime/cgo/abi_amd64.h that handle all necessary registers and setup
and use these macros everywhere on Windows that handles transitions
from C to Go.

Change-Id: I62f41345a507aad1ca383814ac8b7e2a9ffb821e
Reviewed-on: https://go-review.googlesource.com/c/go/+/309769
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: Michael Knyszek <mknyszek@google.com>
src/cmd/internal/obj/x86/obj6.go
src/runtime/cgo/abi_amd64.h [new file with mode: 0644]
src/runtime/cgo/asm_amd64.s
src/runtime/sys_windows_amd64.s

index b8c7ad7d1bb48c8cb86083f3374f60d7abc767df..06736d43bd1696896a4e55985e3ac237e4284df1 100644 (file)
@@ -912,7 +912,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
                }
 
                if autoffset != deltasp {
-                       ctxt.Diag("unbalanced PUSH/POP")
+                       ctxt.Diag("%s: unbalanced PUSH/POP", cursym)
                }
 
                if autoffset != 0 {
diff --git a/src/runtime/cgo/abi_amd64.h b/src/runtime/cgo/abi_amd64.h
new file mode 100644 (file)
index 0000000..44cc096
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Macros for transitioning from the host ABI to Go ABI0.
+//
+// TODO(austin): Define these for ELF platforms as well.
+
+#ifdef GOOS_windows
+
+// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
+// PUSH_REGS_HOST_TO_ABI0.
+#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
+
+// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
+// the host ABI to Go ABI0 code. It saves all registers that are
+// callee-save in the host ABI and caller-save in Go ABI0 and prepares
+// for entry to Go.
+//
+// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
+// Clear the DF flag for the Go ABI.
+// MXCSR matches the Go ABI, so we don't have to set that,
+// and Go doesn't modify it, so we don't have to save it.
+#define PUSH_REGS_HOST_TO_ABI0()       \
+       PUSHFQ                  \
+       CLD                     \
+       ADJSP   $(REGS_HOST_TO_ABI0_STACK - 8)  \
+       MOVQ    DI, (0*0)(SP)   \
+       MOVQ    SI, (1*8)(SP)   \
+       MOVQ    BP, (2*8)(SP)   \
+       MOVQ    BX, (3*8)(SP)   \
+       MOVQ    R12, (4*8)(SP)  \
+       MOVQ    R13, (5*8)(SP)  \
+       MOVQ    R14, (6*8)(SP)  \
+       MOVQ    R15, (7*8)(SP)  \
+       MOVUPS  X6, (8*8)(SP)   \
+       MOVUPS  X7, (10*8)(SP)  \
+       MOVUPS  X8, (12*8)(SP)  \
+       MOVUPS  X9, (14*8)(SP)  \
+       MOVUPS  X10, (16*8)(SP) \
+       MOVUPS  X11, (18*8)(SP) \
+       MOVUPS  X12, (20*8)(SP) \
+       MOVUPS  X13, (22*8)(SP) \
+       MOVUPS  X14, (24*8)(SP) \
+       MOVUPS  X15, (26*8)(SP)
+
+#define POP_REGS_HOST_TO_ABI0()        \
+       MOVQ    (0*0)(SP), DI   \
+       MOVQ    (1*8)(SP), SI   \
+       MOVQ    (2*8)(SP), BP   \
+       MOVQ    (3*8)(SP), BX   \
+       MOVQ    (4*8)(SP), R12  \
+       MOVQ    (5*8)(SP), R13  \
+       MOVQ    (6*8)(SP), R14  \
+       MOVQ    (7*8)(SP), R15  \
+       MOVUPS  (8*8)(SP), X6   \
+       MOVUPS  (10*8)(SP), X7  \
+       MOVUPS  (12*8)(SP), X8  \
+       MOVUPS  (14*8)(SP), X9  \
+       MOVUPS  (16*8)(SP), X10 \
+       MOVUPS  (18*8)(SP), X11 \
+       MOVUPS  (20*8)(SP), X12 \
+       MOVUPS  (22*8)(SP), X13 \
+       MOVUPS  (24*8)(SP), X14 \
+       MOVUPS  (26*8)(SP), X15 \
+       ADJSP   $-(REGS_HOST_TO_ABI0_STACK - 8) \
+       POPFQ
+
+#endif
index 5dc8e2d235ce163433c1af230a0eef5c339854d7..447ddb118df5e279e37d6051570814caece7e75f 100644 (file)
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 #include "textflag.h"
+#include "abi_amd64.h"
 
 // Called by C code generated by cmd/cgo.
 // func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
 // This signature is known to SWIG, so we can't change it.
 #ifndef GOOS_windows
 TEXT crosscall2(SB),NOSPLIT,$0x50-0 /* keeps stack pointer 32-byte aligned */
-#else
-TEXT crosscall2(SB),NOSPLIT,$0x110-0 /* also need to save xmm6 - xmm15 */
-#endif
        MOVQ    BX, 0x18(SP)
        MOVQ    R12, 0x28(SP)
        MOVQ    R13, 0x30(SP)
        MOVQ    R14, 0x38(SP)
        MOVQ    R15, 0x40(SP)
 
-#ifdef GOOS_windows
-       // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 and XMM6 -- XMM15.
-       MOVQ    DI, 0x48(SP)
-       MOVQ    SI, 0x50(SP)
-       MOVUPS  X6, 0x60(SP)
-       MOVUPS  X7, 0x70(SP)
-       MOVUPS  X8, 0x80(SP)
-       MOVUPS  X9, 0x90(SP)
-       MOVUPS  X10, 0xa0(SP)
-       MOVUPS  X11, 0xb0(SP)
-       MOVUPS  X12, 0xc0(SP)
-       MOVUPS  X13, 0xd0(SP)
-       MOVUPS  X14, 0xe0(SP)
-       MOVUPS  X15, 0xf0(SP)
-
-       MOVQ    CX, 0x0(SP)     /* fn */
-       MOVQ    DX, 0x8(SP)     /* arg */
-       // Skip n in R8.
-       MOVQ    R9, 0x10(SP)    /* ctxt */
-
-       CALL    runtime·cgocallback(SB)
-
-       MOVQ    0x48(SP), DI
-       MOVQ    0x50(SP), SI
-       MOVUPS  0x60(SP), X6
-       MOVUPS  0x70(SP), X7
-       MOVUPS  0x80(SP), X8
-       MOVUPS  0x90(SP), X9
-       MOVUPS  0xa0(SP), X10
-       MOVUPS  0xb0(SP), X11
-       MOVUPS  0xc0(SP), X12
-       MOVUPS  0xd0(SP), X13
-       MOVUPS  0xe0(SP), X14
-       MOVUPS  0xf0(SP), X15
-#else
        MOVQ    DI, 0x0(SP)     /* fn */
        MOVQ    SI, 0x8(SP)     /* arg */
        // Skip n in DX.
        MOVQ    CX, 0x10(SP)    /* ctxt */
 
        CALL    runtime·cgocallback(SB)
-#endif
 
        MOVQ    0x18(SP), BX
        MOVQ    0x28(SP), R12
@@ -70,3 +32,21 @@ TEXT crosscall2(SB),NOSPLIT,$0x110-0 /* also need to save xmm6 - xmm15 */
        MOVQ    0x40(SP), R15
 
        RET
+
+#else
+TEXT crosscall2(SB),NOSPLIT,$0-0
+       PUSH_REGS_HOST_TO_ABI0()
+
+       // Make room for arguments to cgocallback.
+       ADJSP   $0x18
+       MOVQ    CX, 0x0(SP)     /* fn */
+       MOVQ    DX, 0x8(SP)     /* arg */
+       // Skip n in R8.
+       MOVQ    R9, 0x10(SP)    /* ctxt */
+
+       CALL    runtime·cgocallback(SB)
+
+       ADJSP   $-0x18
+       POP_REGS_HOST_TO_ABI0()
+       RET
+#endif
index 689633132976465f49a453280d63384d4cbaac61..a9a7dfdd494bb90dbfe9af9e25e37128285bcaec 100644 (file)
@@ -5,6 +5,7 @@
 #include "go_asm.h"
 #include "go_tls.h"
 #include "textflag.h"
+#include "cgo/abi_amd64.h"
 
 // maxargs should be divisible by 2, as Windows stack
 // must be kept 16-byte aligned on syscall entry.
@@ -111,18 +112,10 @@ TEXT runtime·getlasterror(SB),NOSPLIT,$0
 TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0-0
        // CX: PEXCEPTION_POINTERS ExceptionInfo
 
-       // DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
-       // as required by windows callback convention.
-       PUSHFQ
-       SUBQ    $112, SP
-       MOVQ    DI, 80(SP)
-       MOVQ    SI, 72(SP)
-       MOVQ    BP, 64(SP)
-       MOVQ    BX, 56(SP)
-       MOVQ    R12, 48(SP)
-       MOVQ    R13, 40(SP)
-       MOVQ    R14, 32(SP)
-       MOVQ    R15, 88(SP)
+       // 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    AX, R15 // save handler address
 
@@ -138,8 +131,8 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0-0
        CALL    runtime·badsignal2(SB)
 
        // save g and SP in case of stack switch
-       MOVQ    DX, 96(SP) // g
-       MOVQ    SP, 104(SP)
+       MOVQ    DX, 32(SP) // g
+       MOVQ    SP, 40(SP)
 
        // do we need to switch to the g0 stack?
        MOVQ    g_m(DX), BX
@@ -153,9 +146,11 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0-0
        MOVQ    (g_sched+gobuf_sp)(BX), DI
        // make room for sighandler arguments
        // and re-save old SP for restoring later.
-       // (note that the 104(DI) here must match the 104(SP) above.)
-       SUBQ    $120, DI
-       MOVQ    SP, 104(DI)
+       // Adjust g0 stack by the space we're using and
+       // save SP at the same place on the g0 stack.
+       // The 32(DI) here must match the 32(SP) above.
+       SUBQ    $(REGS_HOST_TO_ABI0_STACK + 48), DI
+       MOVQ    SP, 40(DI)
        MOVQ    DI, SP
 
 g0:
@@ -170,23 +165,14 @@ g0:
 
        // switch back to original stack and g
        // no-op if we never left.
-       MOVQ    104(SP), SP
-       MOVQ    96(SP), DX
+       MOVQ    40(SP), SP
+       MOVQ    32(SP), DX
        get_tls(BP)
        MOVQ    DX, g(BP)
 
 done:
-       // restore registers as required for windows callback
-       MOVQ    88(SP), R15
-       MOVQ    32(SP), R14
-       MOVQ    40(SP), R13
-       MOVQ    48(SP), R12
-       MOVQ    56(SP), BX
-       MOVQ    64(SP), BP
-       MOVQ    72(SP), SI
-       MOVQ    80(SP), DI
-       ADDQ    $112, SP
-       POPFQ
+       ADJSP   $-48
+       POP_REGS_HOST_TO_ABI0()
 
        RET
 
@@ -230,21 +216,8 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0
        DIVL    CX
        SUBQ    $1, AX  // subtract 1 because return PC is to the next slot
 
-       // DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
-       // as required by windows callback convention.
-       PUSHFQ
-       SUBQ    $64, SP
-       MOVQ    DI, 56(SP)
-       MOVQ    SI, 48(SP)
-       MOVQ    BP, 40(SP)
-       MOVQ    BX, 32(SP)
-       MOVQ    R12, 24(SP)
-       MOVQ    R13, 16(SP)
-       MOVQ    R14, 8(SP)
-       MOVQ    R15, 0(SP)
-
-       // Go ABI requires DF flag to be cleared.
-       CLD
+       // Switch from the host ABI to the Go ABI.
+       PUSH_REGS_HOST_TO_ABI0()
 
        // Create a struct callbackArgs on our stack to be passed as
        // the "frame" to cgocallback and on to callbackWrap.
@@ -263,23 +236,16 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0
        MOVQ    (24+callbackArgs_result)(SP), AX
        ADDQ    $(24+callbackArgs__size), SP
 
-       // restore registers as required for windows callback
-       MOVQ    0(SP), R15
-       MOVQ    8(SP), R14
-       MOVQ    16(SP), R13
-       MOVQ    24(SP), R12
-       MOVQ    32(SP), BX
-       MOVQ    40(SP), BP
-       MOVQ    48(SP), SI
-       MOVQ    56(SP), DI
-       ADDQ    $64, SP
-       POPFQ
+       POP_REGS_HOST_TO_ABI0()
 
        // The return value was placed in AX above.
        RET
 
 // uint32 tstart_stdcall(M *newm);
 TEXT runtime·tstart_stdcall<ABIInternal>(SB),NOSPLIT,$0
+       // Switch from the host ABI to the Go ABI.
+       PUSH_REGS_HOST_TO_ABI0()
+
        // CX contains first arg newm
        MOVQ    m_g0(CX), DX            // g
 
@@ -298,12 +264,11 @@ TEXT runtime·tstart_stdcall<ABIInternal>(SB),NOSPLIT,$0
        MOVQ    CX, g_m(DX)
        MOVQ    DX, g(SI)
 
-       // Someday the convention will be D is always cleared.
-       CLD
-
        CALL    runtime·stackcheck(SB) // clobbers AX,CX
        CALL    runtime·mstart(SB)
 
+       POP_REGS_HOST_TO_ABI0()
+
        XORL    AX, AX                  // return 0 == success
        RET