1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4//go:build !386 && !arm && !ppc64le && (darwin || freebsd || linux || netbsd || windows)
5
6package purego
7
8import (
9 "runtime"
10 "unsafe"
11)
12
13const (
14 maxArgs = 32
15)
16
17type syscallArgs struct {
18 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
19 a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32 uintptr
20 f1, f2, f3, f4, f5, f6, f7, f8 uintptr
21 arm64_r8 uintptr
22}
23
24func syscall_SyscallN(fn uintptr, sysargs []uintptr, floats []uintptr, r8 uintptr) *syscallArgs {
25 s := thePool.Get().(*syscallArgs)
26 *s = syscallArgs{
27 fn: fn,
28 a1: sysargs[0], a2: sysargs[1], a3: sysargs[2], a4: sysargs[3],
29 a5: sysargs[4], a6: sysargs[5], a7: sysargs[6], a8: sysargs[7],
30 a9: sysargs[8], a10: sysargs[9], a11: sysargs[10], a12: sysargs[11],
31 a13: sysargs[12], a14: sysargs[13], a15: sysargs[14], a16: sysargs[15],
32 a17: sysargs[16], a18: sysargs[17], a19: sysargs[18], a20: sysargs[19],
33 a21: sysargs[20], a22: sysargs[21], a23: sysargs[22], a24: sysargs[23],
34 a25: sysargs[24], a26: sysargs[25], a27: sysargs[26], a28: sysargs[27],
35 a29: sysargs[28], a30: sysargs[29], a31: sysargs[30], a32: sysargs[31],
36 f1: floats[0], f2: floats[1], f3: floats[2], f4: floats[3],
37 f5: floats[4], f6: floats[5], f7: floats[6], f8: floats[7],
38 arm64_r8: r8,
39 }
40 runtime_cgocall(syscallXABI0, unsafe.Pointer(s))
41 return s
42}
43
44// SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
45// There is an internal maximum number of arguments that SyscallN can take. It panics
46// when the maximum is exceeded. It returns the result and the libc error code if there is one.
47//
48// In order to call this function properly make sure to follow all the rules specified in [unsafe.Pointer]
49// especially point 4.
50//
51// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
52// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
53// for an explanation of why that is.
54//
55// On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the
56// stack.
57//
58// The pragma go:nosplit is not needed at this function declaration because it uses go:uintptrescapes
59// which forces all the objects that the uintptrs point to onto the heap where a stack split won't affect
60// their memory location.
61//
62//go:uintptrescapes
63func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
64 if fn == 0 {
65 panic("purego: fn is nil")
66 }
67 if len(args) > maxArgs {
68 panic("purego: too many arguments to SyscallN")
69 }
70
71 // Windows uses syscall.SyscallN in syscall_windows.go.
72 if runtime.GOOS == "windows" {
73 return syscall_syscallN(fn, args...)
74 }
75
76 // add padding so there is no out-of-bounds slicing
77 var tmp [maxArgs]uintptr
78 copy(tmp[:], args)
79 var floats [maxArgs]uintptr
80 copy(floats[:], tmp[:])
81 s := syscall_SyscallN(fn, tmp[:], floats[:], 0)
82 defer thePool.Put(s)
83 return s.a1, s.a2, s.a3
84}