diff options
| author | qmuntal <quimmuntal@gmail.com> | 2025-07-21 14:39:04 +0200 |
|---|---|---|
| committer | Quim Muntal <quimmuntal@gmail.com> | 2025-07-28 09:47:09 -0700 |
| commit | c7ed3a1c5a152d0e610bbbf104dba30099e6942a (patch) | |
| tree | e89ef2d6b5a0bcc10f4174f8e67b88e559febc8e /src/internal/runtime/syscall | |
| parent | e81eac19d30f373496cd1d08ce2f89c0469a21fd (diff) | |
| download | go-c7ed3a1c5a152d0e610bbbf104dba30099e6942a.tar.xz | |
internal/runtime/syscall/windows: factor out code from runtime
Factor out the code related to doing calls using the Windows stdcall
calling convention into a separate package. This will allow us to
reuse it in other low-level packages that can't depend on syscall.
Updates #51087.
Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64,gotip-windows-amd64-longtest,gotip-solaris-amd64
Change-Id: I68640b07091183b50da6bef17406c10a397896e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/689156
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/internal/runtime/syscall')
5 files changed, 343 insertions, 0 deletions
diff --git a/src/internal/runtime/syscall/windows/asm_windows_386.s b/src/internal/runtime/syscall/windows/asm_windows_386.s new file mode 100644 index 0000000000..29cce00309 --- /dev/null +++ b/src/internal/runtime/syscall/windows/asm_windows_386.s @@ -0,0 +1,48 @@ +// Copyright 2025 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·StdCall<ABIInternal>(SB),NOSPLIT,$0 + JMP ·asmstdcall(SB) + +TEXT ·asmstdcall(SB),NOSPLIT,$0 + MOVL fn+0(FP), BX + MOVL SP, BP // save stack pointer + + // SetLastError(0). + MOVL $0, 0x34(FS) + + MOVL StdCallInfo_N(BX), CX + + // Fast version, do not store args on the stack. + CMPL CX, $0 + JE docall + + // Copy args to the stack. + MOVL CX, AX + SALL $2, AX + SUBL AX, SP // room for args + MOVL SP, DI + MOVL StdCallInfo_Args(BX), SI + CLD + REP; MOVSL + +docall: + // Call stdcall or cdecl function. + // DI SI BP BX are preserved, SP is not + CALL StdCallInfo_Fn(BX) + MOVL BP, SP + + // Return result. + MOVL fn+0(FP), BX + MOVL AX, StdCallInfo_R1(BX) + MOVL DX, StdCallInfo_R2(BX) + + // GetLastError(). + MOVL 0x34(FS), AX + MOVL AX, StdCallInfo_Err(BX) + + RET diff --git a/src/internal/runtime/syscall/windows/asm_windows_amd64.s b/src/internal/runtime/syscall/windows/asm_windows_amd64.s new file mode 100644 index 0000000000..c31cbcdd14 --- /dev/null +++ b/src/internal/runtime/syscall/windows/asm_windows_amd64.s @@ -0,0 +1,84 @@ +// Copyright 2025 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. + +#include "go_asm.h" +#include "textflag.h" + +TEXT ·StdCall<ABIInternal>(SB),NOSPLIT,$0 + MOVQ AX, CX + JMP ·asmstdcall(SB) + +TEXT ·asmstdcall(SB),NOSPLIT,$16 + MOVQ SP, AX + ANDQ $~15, SP // alignment as per Windows requirement + MOVQ AX, 8(SP) + MOVQ CX, 0(SP) // asmcgocall will put first argument into CX. + + MOVQ StdCallInfo_Fn(CX), AX + MOVQ StdCallInfo_Args(CX), SI + MOVQ StdCallInfo_N(CX), CX + + // SetLastError(0). + MOVQ 0x30(GS), DI + MOVL $0, 0x68(DI) + + SUBQ $(const_MaxArgs*8), SP // room for args + + // Fast version, do not store args on the stack. + CMPL CX, $0; JE _0args + CMPL CX, $1; JE _1args + CMPL CX, $2; JE _2args + CMPL CX, $3; JE _3args + CMPL CX, $4; JE _4args + + // Check we have enough room for args. + CMPL CX, $const_MaxArgs + JLE 2(PC) + INT $3 // not enough room -> crash + + // Copy args to the stack. + MOVQ SP, DI + CLD + REP; MOVSQ + MOVQ SP, SI + + // Load first 4 args into correspondent registers. + // Floating point arguments are passed in the XMM + // registers. Set them here in case any of the arguments + // are floating point values. For details see + // https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170 +_4args: + MOVQ 24(SI), R9 + MOVQ R9, X3 +_3args: + MOVQ 16(SI), R8 + MOVQ R8, X2 +_2args: + MOVQ 8(SI), DX + MOVQ DX, X1 +_1args: + MOVQ 0(SI), CX + MOVQ CX, X0 +_0args: + + // Call stdcall function. + CALL AX + + ADDQ $(const_MaxArgs*8), SP + + // Return result. + MOVQ 0(SP), CX + MOVQ 8(SP), SP + MOVQ AX, StdCallInfo_R1(CX) + // Floating point return values are returned in XMM0. Setting r2 to this + // value in case this call returned a floating point value. For details, + // see https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention + MOVQ X0, StdCallInfo_R2(CX) + + // GetLastError(). + MOVQ 0x30(GS), DI + MOVL 0x68(DI), AX + MOVQ AX, StdCallInfo_Err(CX) + + RET diff --git a/src/internal/runtime/syscall/windows/asm_windows_arm.s b/src/internal/runtime/syscall/windows/asm_windows_arm.s new file mode 100644 index 0000000000..8cc4d5c9aa --- /dev/null +++ b/src/internal/runtime/syscall/windows/asm_windows_arm.s @@ -0,0 +1,77 @@ +// Copyright 2025 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. + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" +#include "time_windows.h" + +TEXT ·StdCall<ABIInternal>(SB),NOSPLIT,$0 + B ·asmstdcall(SB) + +TEXT ·asmstdcall(SB),NOSPLIT|NOFRAME,$0 + MOVM.DB.W [R4, R5, R14], (R13) // push {r4, r5, lr} + MOVW R0, R4 // put fn * in r4 + MOVW R13, R5 // save stack pointer in r5 + + // SetLastError(0) + MOVW $0, R0 + MRC 15, 0, R1, C13, C0, 2 + MOVW R0, 0x34(R1) + + MOVW 8(R4), R12 // fn->Args + + // Do we have more than 4 arguments? + MOVW 4(R4), R0 // fn->n + SUB.S $4, R0, R2 + BLE loadregs + + // Reserve stack space for remaining args + SUB R2<<2, R13 + BIC $0x7, R13 // alignment for ABI + + // R0: count of arguments + // R1: + // R2: loop counter, from 0 to (n-4) + // R3: scratch + // R4: pointer to StdCallInfo struct + // R12: fn->args + MOVW $0, R2 +stackargs: + ADD $4, R2, R3 // r3 = args[4 + i] + MOVW R3<<2(R12), R3 + MOVW R3, R2<<2(R13) // stack[i] = r3 + + ADD $1, R2 // i++ + SUB $4, R0, R3 // while (i < (n - 4)) + CMP R3, R2 + BLT stackargs + +loadregs: + CMP $3, R0 + MOVW.GT 12(R12), R3 + + CMP $2, R0 + MOVW.GT 8(R12), R2 + + CMP $1, R0 + MOVW.GT 4(R12), R1 + + CMP $0, R0 + MOVW.GT 0(R12), R0 + + BIC $0x7, R13 // alignment for ABI + MOVW 0(R4), R12 // branch to fn->fn + BL (R12) + + MOVW R5, R13 // free stack space + MOVW R0, 12(R4) // save return value to fn->r1 + MOVW R1, 16(R4) + + // GetLastError + MRC 15, 0, R1, C13, C0, 2 + MOVW 0x34(R1), R0 + MOVW R0, 20(R4) // store in fn->err + + MOVM.IA.W (R13), [R4, R5, R15] diff --git a/src/internal/runtime/syscall/windows/asm_windows_arm64.s b/src/internal/runtime/syscall/windows/asm_windows_arm64.s new file mode 100644 index 0000000000..fb4cda0f83 --- /dev/null +++ b/src/internal/runtime/syscall/windows/asm_windows_arm64.s @@ -0,0 +1,90 @@ +// Copyright 2025 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. + +#include "go_asm.h" +#include "textflag.h" + +// Offsets into Thread Environment Block (pointer in R18) +#define TEB_error 0x68 + +TEXT ·StdCall<ABIInternal>(SB),NOSPLIT,$0 + B ·asmstdcall(SB) + +TEXT ·asmstdcall(SB),NOSPLIT,$16 + STP (R19, R20), 16(RSP) // save old R19, R20 + MOVD R0, R19 // save fn pointer + MOVD RSP, R20 // save stack pointer + + // SetLastError(0) + MOVD $0, TEB_error(R18_PLATFORM) + MOVD StdCallInfo_Args(R19), R12 + + // Do we have more than 8 arguments? + MOVD StdCallInfo_N(R19), R0 + CMP $0, R0; BEQ _0args + CMP $1, R0; BEQ _1args + CMP $2, R0; BEQ _2args + CMP $3, R0; BEQ _3args + CMP $4, R0; BEQ _4args + CMP $5, R0; BEQ _5args + CMP $6, R0; BEQ _6args + CMP $7, R0; BEQ _7args + CMP $8, R0; BEQ _8args + + // Reserve stack space for remaining args + SUB $8, R0, R2 + ADD $1, R2, R3 // make even number of words for stack alignment + AND $~1, R3 + LSL $3, R3 + SUB R3, RSP + + // R4: size of stack arguments (n-8)*8 + // R5: &args[8] + // R6: loop counter, from 0 to (n-8)*8 + // R7: scratch + // R8: copy of RSP - (R2)(RSP) assembles as (R2)(ZR) + SUB $8, R0, R4 + LSL $3, R4 + ADD $(8*8), R12, R5 + MOVD $0, R6 + MOVD RSP, R8 +stackargs: + MOVD (R6)(R5), R7 + MOVD R7, (R6)(R8) + ADD $8, R6 + CMP R6, R4 + BNE stackargs + +_8args: + MOVD (7*8)(R12), R7 +_7args: + MOVD (6*8)(R12), R6 +_6args: + MOVD (5*8)(R12), R5 +_5args: + MOVD (4*8)(R12), R4 +_4args: + MOVD (3*8)(R12), R3 +_3args: + MOVD (2*8)(R12), R2 +_2args: + MOVD (1*8)(R12), R1 +_1args: + MOVD (0*8)(R12), R0 +_0args: + + MOVD StdCallInfo_Fn(R19), R12 + BL (R12) + + MOVD R20, RSP // free stack space + MOVD R0, StdCallInfo_R1(R19) // save return value + // TODO(rsc) floating point like amd64 in StdCallInfo_R2? + + // GetLastError + MOVD TEB_error(R18_PLATFORM), R0 + MOVD R0, StdCallInfo_Err(R19) + + // Restore callee-saved registers. + LDP 16(RSP), (R19, R20) + RET diff --git a/src/internal/runtime/syscall/windows/syscall_windows.go b/src/internal/runtime/syscall/windows/syscall_windows.go new file mode 100644 index 0000000000..0d350f0d7f --- /dev/null +++ b/src/internal/runtime/syscall/windows/syscall_windows.go @@ -0,0 +1,44 @@ +// Copyright 2025 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. + +// Package windows provides the syscall primitives required for the runtime. + +package windows + +import ( + "internal/abi" +) + +// MaxArgs should be divisible by 2, as Windows stack +// must be kept 16-byte aligned on syscall entry. +// +// Although it only permits maximum 42 parameters, it +// is arguably large enough. +const MaxArgs = 42 + +// StdCallInfo is a structure used to pass parameters to the system call. +type StdCallInfo struct { + Fn uintptr + N uintptr // number of parameters + Args uintptr // parameters + R1 uintptr // return values + R2 uintptr + Err uintptr // error number +} + +// StdCall calls a function using Windows' stdcall convention. +// +//go:noescape +func StdCall(fn *StdCallInfo) + +// asmstdcall is the function pointer for [AsmStdCallAddr]. +func asmstdcall(fn *StdCallInfo) + +// AsmStdCallAddr is the address of a function that accepts a pointer +// to [StdCallInfo] stored on the stack following the C calling convention, +// and calls the function using Windows' stdcall calling convention. +// Shouldn't be called directly from Go. +func AsmStdCallAddr() uintptr { + return abi.FuncPCABI0(asmstdcall) +} |
