compiler, runtime: fix SyscallN handling for Windows on Go 1.26+

Go 1.26 changed all Windows syscall wrappers in zsyscall_windows.go
to use SyscallN instead of fixed-argument Syscall/Syscall6/etc. The
SyscallN function now has a body that calls an unexported syscalln
function (provided by runtime via //go:linkname).

TinyGo's existing createSyscall compiler builtin used call.Args[2:]
to extract syscall arguments, but for variadic SyscallN the SSA
representation passes args as a slice value (not individual args),
causing call.Args[2:] to be empty -- resulting in zero arguments
being passed to Windows API calls and 0xc0000005 access violations.

Fix this by:
1. Excluding syscall.SyscallN from builtin interception, letting
   Go 1.26's function body compile normally (it calls syscalln)
2. Adding a new createSyscalln compiler builtin that intercepts
   syscall.syscalln and correctly handles the variadic slice:
   - Generates a switch on the arg count n (0-18 cases)
   - Each case loads args from the slice via GEP/Load
   - Wraps calls with SetLastError(0)/GetLastError() as before
   - Handles i386 stdcall conventions
3. Adding runtime stubs for both Go versions:
   - go1.26: syscall.syscalln stub (body intercepted by compiler)
   - pre-go1.26: syscall.SyscallN stub (linker satisfaction)
This commit is contained in:
deadprogram
2026-04-10 12:15:58 +02:00
committed by Ron Evans
parent 2d477e069d
commit ddacdfacd5
4 changed files with 164 additions and 1 deletions
+18
View File
@@ -0,0 +1,18 @@
//go:build windows && !go1.26
package runtime
// For Go < 1.26, SyscallN is declared without a body in
// syscall/dll_windows.go. The standard Go runtime provides its implementation
// via //go:linkname. TinyGo must provide one as well.
//
// Note: The TinyGo compiler cannot correctly handle SyscallN as a builtin
// because it uses variadic arguments represented as a slice in SSA, which
// the fixed-argument builtin mechanism cannot process. This implementation
// is provided to satisfy the linker for code paths that reference SyscallN
// (e.g., syscall.Proc.Call).
//go:linkname syscall_SyscallN syscall.SyscallN
func syscall_SyscallN(trap uintptr, args ...uintptr) (r1, r2, err uintptr) {
panic("syscall.SyscallN is not yet supported in TinyGo for Go < 1.26")
}
+17
View File
@@ -0,0 +1,17 @@
//go:build windows && go1.26
package runtime
// Starting with Go 1.26, the syscall package on Windows defines function bodies
// for Syscall, SyscallN, etc., that all call syscalln (lowercase). In standard
// Go, syscalln is provided by the runtime via //go:linkname. TinyGo's compiler
// intercepts calls to syscall.syscalln and replaces them with inline LLVM IR
// (see compiler/syscall.go createSyscalln), so this function body is never
// actually called at runtime. However, the compiled function bodies in the
// syscall package still reference it, so we must provide a definition to
// satisfy the linker.
//go:linkname syscall_syscalln syscall.syscalln
func syscall_syscalln(fn, n uintptr, args ...uintptr) (r1, r2, err uintptr) {
panic("unreachable: syscall.syscalln should be handled by the compiler")
}