]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: add SyscallN
authorChangkun Ou <hi@changkun.de>
Thu, 22 Jul 2021 15:50:42 +0000 (17:50 +0200)
committerMatthew Dempsky <mdempsky@google.com>
Thu, 19 Aug 2021 17:30:19 +0000 (17:30 +0000)
This CL adds a new syscall.SyscallN API.

The proposal discussion also suggests the API should not only for
Windows but other platforms. However, the existing API set already
contain differences between platforms, hence the CL only implements
the Windows platform.

Moreover, although the API offers variadic parameters, the permitted
parameters remains up to a limit, which is selected as 42, and arguably
large enough.

Fixes #46552

Change-Id: I66b49988a304d9fc178c7cd5de46d0b75e167a4f
Reviewed-on: https://go-review.googlesource.com/c/go/+/336550
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>

doc/go1.18.html
src/runtime/export_windows_test.go
src/runtime/sys_windows_amd64.s
src/runtime/syscall_windows.go
src/runtime/syscall_windows_test.go
src/syscall/dll_windows.go

index 2dc4c1427dc6c8102e2e7477c7228faef8a2689b..df4b56c49fbd97fed1dacafb0e8891bd8f1bf247 100644 (file)
@@ -82,3 +82,20 @@ Do not send CLs removing the interior tags from such phrases.
 <p>
   TODO: complete this section
 </p>
+
+<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
+  <dd>
+    <p><!-- CL 336550 -->
+      The new function <a href="/pkg/syscall/?GOOS=windows#SyscallN"><code>SyscallN</code></a>
+      has been introduced for Windows, allowing for calls with arbitrary number
+      of arguments. As results,
+      <a href="/pkg/syscall/?GOOS=windows#Syscall"><code>Syscall</code></a>,
+      <a href="/pkg/syscall/?GOOS=windows#Syscall6"><code>Syscall6</code></a>,
+      <a href="/pkg/syscall/?GOOS=windows#Syscall9"><code>Syscall9</code></a>,
+      <a href="/pkg/syscall/?GOOS=windows#Syscall12"><code>Syscall12</code></a>,
+      <a href="/pkg/syscall/?GOOS=windows#Syscall15"><code>Syscall15</code></a>, and
+      <a href="/pkg/syscall/?GOOS=windows#Syscall18"><code>Syscall18</code></a> are
+      deprecated in favor of <a href="/pkg/syscall/?GOOS=windows#SyscallN"><code>SyscallN</code></a>.
+    </p>
+  </dd>
+</dl><!-- syscall -->
\ No newline at end of file
index 536b398fd7a077802aa5a65a328237c049171afa..d9cf753463940c2f6c2bfc0c8dd3a9de56179411 100644 (file)
@@ -8,6 +8,8 @@ package runtime
 
 import "unsafe"
 
+const MaxArgs = maxArgs
+
 var (
        TestingWER              = &testingWER
        OsYield                 = osyield
index 6cc5bba2b7378b4dd76d53965bc88a1b2c1ca505..1e4c1d2b6117b28ad61934013ff7007e40c5eedd 100644 (file)
@@ -8,10 +8,6 @@
 #include "time_windows.h"
 #include "cgo/abi_amd64.h"
 
-// maxargs should be divisible by 2, as Windows stack
-// must be kept 16-byte aligned on syscall entry.
-#define maxargs 18
-
 // void runtime·asmstdcall(void *c);
 TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0
        // asmcgocall will put first argument into CX.
@@ -24,14 +20,14 @@ TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0
        MOVQ    0x30(GS), DI
        MOVL    $0, 0x68(DI)
 
-       SUBQ    $(maxargs*8), SP        // room for args
+       SUBQ    $(const_maxArgs*8), SP  // room for args
 
        // Fast version, do not store args on the stack.
        CMPL    CX, $4
        JLE     loadregs
 
        // Check we have enough room for args.
-       CMPL    CX, $maxargs
+       CMPL    CX, $const_maxArgs
        JLE     2(PC)
        INT     $3                      // not enough room -> crash
 
@@ -59,7 +55,7 @@ loadregs:
        // Call stdcall function.
        CALL    AX
 
-       ADDQ    $(maxargs*8), SP
+       ADDQ    $(const_maxArgs*8), SP
 
        // Return result.
        POPQ    CX
index e872d74e97fc6cded5ffcdaa02f58c1181578223..da181f2a8d416ad9aeacc0de4974da17f23881ff 100644 (file)
@@ -468,84 +468,69 @@ func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uint
 
 //go:linkname syscall_Syscall syscall.Syscall
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
-       lockOSThread()
-       defer unlockOSThread()
-       c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
-       cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       return c.r1, c.r2, c.err
+       return syscall_SyscallN(fn, a1, a2, a3)
 }
 
 //go:linkname syscall_Syscall6 syscall.Syscall6
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
-       lockOSThread()
-       defer unlockOSThread()
-       c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
-       cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       return c.r1, c.r2, c.err
+       return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6)
 }
 
 //go:linkname syscall_Syscall9 syscall.Syscall9
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
-       lockOSThread()
-       c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
-       cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       unlockOSThread()
-       return c.r1, c.r2, c.err
+       return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9)
 }
 
 //go:linkname syscall_Syscall12 syscall.Syscall12
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) {
-       lockOSThread()
-       c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
-       cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       unlockOSThread()
-       return c.r1, c.r2, c.err
+       return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)
 }
 
 //go:linkname syscall_Syscall15 syscall.Syscall15
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
-       lockOSThread()
-       c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
-       cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       unlockOSThread()
-       return c.r1, c.r2, c.err
+       return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)
 }
 
 //go:linkname syscall_Syscall18 syscall.Syscall18
 //go:nosplit
-//go:cgo_unsafe_args
 func syscall_Syscall18(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2, err uintptr) {
+       return syscall_SyscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18)
+}
+
+// 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
+
+//go:linkname syscall_SyscallN syscall.SyscallN
+//go:nosplit
+func syscall_SyscallN(trap uintptr, args ...uintptr) (r1, r2, err uintptr) {
+       nargs := len(args)
+
+       // asmstdcall expects it can access the first 4 arguments
+       // to load them into registers.
+       var tmp [4]uintptr
+       switch {
+       case nargs < 4:
+               copy(tmp[:], args)
+               args = tmp[:]
+       case nargs > maxArgs:
+               panic("runtime: SyscallN has too many arguments")
+       }
+
        lockOSThread()
+       defer unlockOSThread()
        c := &getg().m.syscall
-       c.fn = fn
-       c.n = nargs
-       c.args = uintptr(noescape(unsafe.Pointer(&a1)))
+       c.fn = trap
+       c.n = uintptr(nargs)
+       c.args = uintptr(noescape(unsafe.Pointer(&args[0])))
        cgocall(asmstdcallAddr, unsafe.Pointer(c))
-       unlockOSThread()
        return c.r1, c.r2, c.err
 }
index e3f772ac4bb9457a5ed94ff944cfc466b67afca2..235c79f68ffe9c0affe3085154d2ab5472298021 100644 (file)
@@ -759,7 +759,7 @@ uintptr_t cfunc(callback f, uintptr_t n) {
        }
 }
 
-func TestSyscall18(t *testing.T) {
+func TestSyscallN(t *testing.T) {
        if _, err := exec.LookPath("gcc"); err != nil {
                t.Skip("skipping test: gcc is missing")
        }
@@ -767,40 +767,52 @@ func TestSyscall18(t *testing.T) {
                t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
        }
 
-       const src = `
-#include <stdint.h>
-#include <windows.h>
+       for arglen := 0; arglen <= runtime.MaxArgs; arglen++ {
+               arglen := arglen
+               t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
+                       t.Parallel()
+                       args := make([]string, arglen)
+                       rets := make([]string, arglen+1)
+                       params := make([]uintptr, arglen)
+                       for i := range args {
+                               args[i] = fmt.Sprintf("int a%d", i)
+                               rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
+                               params[i] = uintptr(i)
+                       }
+                       rets[arglen] = "1" // for arglen == 0
 
-int cfunc(     int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9,
-                       int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18) {
-       return 1;
-}
-`
-       tmpdir := t.TempDir()
+                       src := fmt.Sprintf(`
+               #include <stdint.h>
+               #include <windows.h>
+               int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
 
-       srcname := "mydll.c"
-       err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
-       if err != nil {
-               t.Fatal(err)
-       }
-       outname := "mydll.dll"
-       cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
-       cmd.Dir = tmpdir
-       out, err := cmd.CombinedOutput()
-       if err != nil {
-               t.Fatalf("failed to build dll: %v - %v", err, string(out))
-       }
-       dllpath := filepath.Join(tmpdir, outname)
+                       tmpdir := t.TempDir()
 
-       dll := syscall.MustLoadDLL(dllpath)
-       defer dll.Release()
+                       srcname := "mydll.c"
+                       err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       outname := "mydll.dll"
+                       cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
+                       cmd.Dir = tmpdir
+                       out, err := cmd.CombinedOutput()
+                       if err != nil {
+                               t.Fatalf("failed to build dll: %v\n%s", err, out)
+                       }
+                       dllpath := filepath.Join(tmpdir, outname)
 
-       proc := dll.MustFindProc("cfunc")
+                       dll := syscall.MustLoadDLL(dllpath)
+                       defer dll.Release()
 
-       // proc.Call() will call Syscall18() internally.
-       r, _, err := proc.Call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)
-       if r != 1 {
-               t.Errorf("got %d want 1 (err=%v)", r, err)
+                       proc := dll.MustFindProc("cfunc")
+
+                       // proc.Call() will call SyscallN() internally.
+                       r, _, err := proc.Call(params...)
+                       if r != 1 {
+                               t.Errorf("got %d want 1 (err=%v)", r, err)
+                       }
+               })
        }
 }
 
index 16210ca5b58a53d1cd26bec6374b3b17818feb7f..34b481d6e63543c1cf08cf1ea0d2ec664b66781a 100644 (file)
@@ -5,7 +5,6 @@
 package syscall
 
 import (
-       "internal/itoa"
        "internal/syscall/windows/sysdll"
        "sync"
        "sync/atomic"
@@ -25,12 +24,25 @@ func (e *DLLError) Unwrap() error { return e.Err }
 
 // Implemented in ../runtime/syscall_windows.go.
 
+// Deprecated: Use SyscallN instead.
 func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
+
+// Deprecated: Use SyscallN instead.
 func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
+
+// Deprecated: Use SyscallN instead.
 func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
+
+// Deprecated: Use SyscallN instead.
 func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
+
+// Deprecated: Use SyscallN instead.
 func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
+
+// Deprecated: Use SyscallN instead.
 func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
+
+func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
 func loadlibrary(filename *uint16) (handle uintptr, err Errno)
 func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)
 func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
@@ -160,8 +172,7 @@ func (p *Proc) Addr() uintptr {
 
 //go:uintptrescapes
 
-// Call executes procedure p with arguments a. It will panic if more than 18 arguments
-// are supplied.
+// Call executes procedure p with arguments a.
 //
 // The returned error is always non-nil, constructed from the result of GetLastError.
 // Callers must inspect the primary return value to decide whether an error occurred
@@ -175,49 +186,8 @@ func (p *Proc) Addr() uintptr {
 // values are returned in r2. The return value for C type "float" is
 // math.Float32frombits(uint32(r2)). For C type "double", it is
 // math.Float64frombits(uint64(r2)).
-func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
-       switch len(a) {
-       case 0:
-               return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
-       case 1:
-               return Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
-       case 2:
-               return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
-       case 3:
-               return Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
-       case 4:
-               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
-       case 5:
-               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
-       case 6:
-               return Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
-       case 7:
-               return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
-       case 8:
-               return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
-       case 9:
-               return Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
-       case 10:
-               return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
-       case 11:
-               return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
-       case 12:
-               return Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
-       case 13:
-               return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
-       case 14:
-               return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
-       case 15:
-               return Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
-       case 16:
-               return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], 0, 0)
-       case 17:
-               return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], 0)
-       case 18:
-               return Syscall18(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17])
-       default:
-               panic("Call " + p.Name + " with too many arguments " + itoa.Itoa(len(a)) + ".")
-       }
+func (p *Proc) Call(a ...uintptr) (uintptr, uintptr, error) {
+       return SyscallN(p.Addr(), a...)
 }
 
 // A LazyDLL implements access to a single DLL.