-
Notifications
You must be signed in to change notification settings - Fork 70
/
syscall_sysv.go
223 lines (206 loc) · 6.71 KB
/
syscall_sysv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
//go:build darwin || freebsd || (linux && (amd64 || arm64))
package purego
import (
"reflect"
"runtime"
"sync"
"unsafe"
)
var syscall15XABI0 uintptr
//go:nosplit
func syscall_syscall15X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) {
args := syscall15Args{
fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
a1, a2, a3, a4, a5, a6, a7, a8,
0,
}
runtime_cgocall(syscall15XABI0, unsafe.Pointer(&args))
return args.a1, args.a2, 0
}
// NewCallback converts a Go function to a function pointer conforming to the C calling convention.
// This is useful when interoperating with C code requiring callbacks. The argument is expected to be a
// function with zero or one uintptr-sized result. The function must not have arguments with size larger than the size
// of uintptr. Only a limited number of callbacks may be created in a single Go process, and any memory allocated
// for these callbacks is never released. At least 2000 callbacks can always be created. Although this function
// provides similar functionality to windows.NewCallback it is distinct.
func NewCallback(fn any) uintptr {
ty := reflect.TypeOf(fn)
for i := 0; i < ty.NumIn(); i++ {
in := ty.In(i)
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
continue
}
if i != 0 {
panic("purego: CDecl must be the first argument")
}
}
return compileCallback(fn)
}
// maxCb is the maximum number of callbacks
// only increase this if you have added more to the callbackasm function
const maxCB = 2000
var cbs struct {
lock sync.Mutex
numFn int // the number of functions currently in cbs.funcs
funcs [maxCB]reflect.Value // the saved callbacks
}
type callbackArgs struct {
index uintptr
// args points to the argument block.
//
// The structure of the arguments goes
// float registers followed by the
// integer registers followed by the stack.
//
// This variable is treated as a continuous
// block of memory containing all of the arguments
// for this callback.
args unsafe.Pointer
// Below are out-args from callbackWrap
result uintptr
}
func compileCallback(fn any) uintptr {
val := reflect.ValueOf(fn)
if val.Kind() != reflect.Func {
panic("purego: the type must be a function but was not")
}
if val.IsNil() {
panic("purego: function must not be nil")
}
ty := val.Type()
for i := 0; i < ty.NumIn(); i++ {
in := ty.In(i)
switch in.Kind() {
case reflect.Struct:
if i == 0 && in.AssignableTo(reflect.TypeOf(CDecl{})) {
continue
}
fallthrough
case reflect.Interface, reflect.Func, reflect.Slice,
reflect.Chan, reflect.Complex64, reflect.Complex128,
reflect.String, reflect.Map, reflect.Invalid:
panic("purego: unsupported argument type: " + in.Kind().String())
}
}
output:
switch {
case ty.NumOut() == 1:
switch ty.Out(0).Kind() {
case reflect.Pointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
reflect.Bool, reflect.UnsafePointer:
break output
}
panic("purego: unsupported return type: " + ty.String())
case ty.NumOut() > 1:
panic("purego: callbacks can only have one return")
}
cbs.lock.Lock()
defer cbs.lock.Unlock()
if cbs.numFn >= maxCB {
panic("purego: the maximum number of callbacks has been reached")
}
cbs.funcs[cbs.numFn] = val
cbs.numFn++
return callbackasmAddr(cbs.numFn - 1)
}
const ptrSize = unsafe.Sizeof((*int)(nil))
const callbackMaxFrame = 64 * ptrSize
// callbackasm is implemented in zcallback_GOOS_GOARCH.s
//
//go:linkname __callbackasm callbackasm
var __callbackasm byte
var callbackasmABI0 = uintptr(unsafe.Pointer(&__callbackasm))
// callbackWrap_call allows the calling of the ABIInternal wrapper
// which is required for runtime.cgocallback without the
// <ABIInternal> tag which is only allowed in the runtime.
// This closure is used inside sys_darwin_GOARCH.s
var callbackWrap_call = callbackWrap
// callbackWrap is called by assembly code which determines which Go function to call.
// This function takes the arguments and passes them to the Go function and returns the result.
func callbackWrap(a *callbackArgs) {
cbs.lock.Lock()
fn := cbs.funcs[a.index]
cbs.lock.Unlock()
fnType := fn.Type()
args := make([]reflect.Value, fnType.NumIn())
frame := (*[callbackMaxFrame]uintptr)(a.args)
var floatsN int // floatsN represents the number of float arguments processed
var intsN int // intsN represents the number of integer arguments processed
// stack points to the index into frame of the current stack element.
// The stack begins after the float and integer registers.
stack := numOfIntegerRegisters() + numOfFloats
for i := range args {
var pos int
switch fnType.In(i).Kind() {
case reflect.Float32, reflect.Float64:
if floatsN >= numOfFloats {
pos = stack
stack++
} else {
pos = floatsN
}
floatsN++
case reflect.Struct:
// This is the CDecl field
args[i] = reflect.Zero(fnType.In(i))
continue
default:
if intsN >= numOfIntegerRegisters() {
pos = stack
stack++
} else {
// the integers begin after the floats in frame
pos = intsN + numOfFloats
}
intsN++
}
args[i] = reflect.NewAt(fnType.In(i), unsafe.Pointer(&frame[pos])).Elem()
}
ret := fn.Call(args)
if len(ret) > 0 {
switch k := ret[0].Kind(); k {
case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uintptr:
a.result = uintptr(ret[0].Uint())
case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
a.result = uintptr(ret[0].Int())
case reflect.Bool:
if ret[0].Bool() {
a.result = 1
} else {
a.result = 0
}
case reflect.Pointer:
a.result = ret[0].Pointer()
case reflect.UnsafePointer:
a.result = ret[0].Pointer()
default:
panic("purego: unsupported kind: " + k.String())
}
}
}
// callbackasmAddr returns address of runtime.callbackasm
// function adjusted by i.
// On x86 and amd64, runtime.callbackasm is a series of CALL instructions,
// and we want callback to arrive at
// correspondent call instruction instead of start of
// runtime.callbackasm.
// On ARM, runtime.callbackasm is a series of mov and branch instructions.
// R12 is loaded with the callback index. Each entry is two instructions,
// hence 8 bytes.
func callbackasmAddr(i int) uintptr {
var entrySize int
switch runtime.GOARCH {
default:
panic("purego: unsupported architecture")
case "386", "amd64":
entrySize = 5
case "arm", "arm64":
// On ARM and ARM64, each entry is a MOV instruction
// followed by a branch instruction
entrySize = 8
}
return callbackasmABI0 + uintptr(i*entrySize)
}