/src/glib-2.80.0/subprojects/libffi/src/x86/ffiw64.c
Line | Count | Source |
1 | | /* ----------------------------------------------------------------------- |
2 | | ffiw64.c - Copyright (c) 2018 Anthony Green |
3 | | Copyright (c) 2014 Red Hat, Inc. |
4 | | |
5 | | x86 win64 Foreign Function Interface |
6 | | |
7 | | Permission is hereby granted, free of charge, to any person obtaining |
8 | | a copy of this software and associated documentation files (the |
9 | | ``Software''), to deal in the Software without restriction, including |
10 | | without limitation the rights to use, copy, modify, merge, publish, |
11 | | distribute, sublicense, and/or sell copies of the Software, and to |
12 | | permit persons to whom the Software is furnished to do so, subject to |
13 | | the following conditions: |
14 | | |
15 | | The above copyright notice and this permission notice shall be included |
16 | | in all copies or substantial portions of the Software. |
17 | | |
18 | | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, |
19 | | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
20 | | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
21 | | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
22 | | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
23 | | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
25 | | DEALINGS IN THE SOFTWARE. |
26 | | ----------------------------------------------------------------------- */ |
27 | | |
28 | | #include <ffi.h> |
29 | | #include <ffi_common.h> |
30 | | #include <stdlib.h> |
31 | | #include <stdint.h> |
32 | | |
33 | | #ifdef X86_WIN64 |
34 | | #define EFI64(name) name |
35 | | #else |
36 | | #define EFI64(name) name##_efi64 |
37 | | #endif |
38 | | |
39 | | struct win64_call_frame |
40 | | { |
41 | | UINT64 rbp; /* 0 */ |
42 | | UINT64 retaddr; /* 8 */ |
43 | | UINT64 fn; /* 16 */ |
44 | | UINT64 flags; /* 24 */ |
45 | | UINT64 rvalue; /* 32 */ |
46 | | }; |
47 | | |
48 | | extern void ffi_call_win64 (void *stack, struct win64_call_frame *, |
49 | | void *closure) FFI_HIDDEN; |
50 | | |
51 | | ffi_status |
52 | | EFI64(ffi_prep_cif_machdep)(ffi_cif *cif) |
53 | 0 | { |
54 | 0 | int flags, n; |
55 | |
|
56 | 0 | switch (cif->abi) |
57 | 0 | { |
58 | 0 | case FFI_WIN64: |
59 | 0 | case FFI_GNUW64: |
60 | 0 | break; |
61 | 0 | default: |
62 | 0 | return FFI_BAD_ABI; |
63 | 0 | } |
64 | | |
65 | 0 | flags = cif->rtype->type; |
66 | 0 | switch (flags) |
67 | 0 | { |
68 | 0 | default: |
69 | 0 | break; |
70 | 0 | case FFI_TYPE_LONGDOUBLE: |
71 | | /* GCC returns long double values by reference, like a struct */ |
72 | 0 | if (cif->abi == FFI_GNUW64) |
73 | 0 | flags = FFI_TYPE_STRUCT; |
74 | 0 | break; |
75 | 0 | case FFI_TYPE_COMPLEX: |
76 | 0 | flags = FFI_TYPE_STRUCT; |
77 | | /* FALLTHRU */ |
78 | 0 | case FFI_TYPE_STRUCT: |
79 | 0 | switch (cif->rtype->size) |
80 | 0 | { |
81 | 0 | case 8: |
82 | 0 | flags = FFI_TYPE_UINT64; |
83 | 0 | break; |
84 | 0 | case 4: |
85 | 0 | flags = FFI_TYPE_SMALL_STRUCT_4B; |
86 | 0 | break; |
87 | 0 | case 2: |
88 | 0 | flags = FFI_TYPE_SMALL_STRUCT_2B; |
89 | 0 | break; |
90 | 0 | case 1: |
91 | 0 | flags = FFI_TYPE_SMALL_STRUCT_1B; |
92 | 0 | break; |
93 | 0 | } |
94 | 0 | break; |
95 | 0 | } |
96 | 0 | cif->flags = flags; |
97 | | |
98 | | /* Each argument either fits in a register, an 8 byte slot, or is |
99 | | passed by reference with the pointer in the 8 byte slot. */ |
100 | 0 | n = cif->nargs; |
101 | 0 | n += (flags == FFI_TYPE_STRUCT); |
102 | 0 | if (n < 4) |
103 | 0 | n = 4; |
104 | 0 | cif->bytes = n * 8; |
105 | |
|
106 | 0 | return FFI_OK; |
107 | 0 | } |
108 | | |
109 | | /* We perform some black magic here to use some of the parent's stack frame in |
110 | | * ffi_call_win64() that breaks with the MSVC compiler with the /RTCs or /GZ |
111 | | * flags. Disable the 'Stack frame run time error checking' for this function |
112 | | * so we don't hit weird exceptions in debug builds. */ |
113 | | #if defined(_MSC_VER) |
114 | | #pragma runtime_checks("s", off) |
115 | | #endif |
116 | | static void |
117 | | ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, |
118 | | void **avalue, void *closure) |
119 | 0 | { |
120 | 0 | int i, j, n, flags; |
121 | 0 | UINT64 *stack; |
122 | 0 | size_t rsize; |
123 | 0 | struct win64_call_frame *frame; |
124 | |
|
125 | 0 | FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64); |
126 | |
|
127 | 0 | flags = cif->flags; |
128 | 0 | rsize = 0; |
129 | | |
130 | | /* If we have no return value for a structure, we need to create one. |
131 | | Otherwise we can ignore the return type entirely. */ |
132 | 0 | if (rvalue == NULL) |
133 | 0 | { |
134 | 0 | if (flags == FFI_TYPE_STRUCT) |
135 | 0 | rsize = cif->rtype->size; |
136 | 0 | else |
137 | 0 | flags = FFI_TYPE_VOID; |
138 | 0 | } |
139 | |
|
140 | 0 | stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize); |
141 | 0 | frame = (struct win64_call_frame *)((char *)stack + cif->bytes); |
142 | 0 | if (rsize) |
143 | 0 | rvalue = frame + 1; |
144 | |
|
145 | 0 | frame->fn = (uintptr_t)fn; |
146 | 0 | frame->flags = flags; |
147 | 0 | frame->rvalue = (uintptr_t)rvalue; |
148 | |
|
149 | 0 | j = 0; |
150 | 0 | if (flags == FFI_TYPE_STRUCT) |
151 | 0 | { |
152 | 0 | stack[0] = (uintptr_t)rvalue; |
153 | 0 | j = 1; |
154 | 0 | } |
155 | |
|
156 | 0 | for (i = 0, n = cif->nargs; i < n; ++i, ++j) |
157 | 0 | { |
158 | 0 | switch (cif->arg_types[i]->size) |
159 | 0 | { |
160 | 0 | case 8: |
161 | 0 | stack[j] = *(UINT64 *)avalue[i]; |
162 | 0 | break; |
163 | 0 | case 4: |
164 | 0 | stack[j] = *(UINT32 *)avalue[i]; |
165 | 0 | break; |
166 | 0 | case 2: |
167 | 0 | stack[j] = *(UINT16 *)avalue[i]; |
168 | 0 | break; |
169 | 0 | case 1: |
170 | 0 | stack[j] = *(UINT8 *)avalue[i]; |
171 | 0 | break; |
172 | 0 | default: |
173 | 0 | stack[j] = (uintptr_t)avalue[i]; |
174 | 0 | break; |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | 0 | ffi_call_win64 (stack, frame, closure); |
179 | 0 | } |
180 | | #if defined(_MSC_VER) |
181 | | #pragma runtime_checks("s", restore) |
182 | | #endif |
183 | | |
184 | | void |
185 | | EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) |
186 | 0 | { |
187 | 0 | ffi_call_int (cif, fn, rvalue, avalue, NULL); |
188 | 0 | } |
189 | | |
190 | | void |
191 | | EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue, |
192 | | void **avalue, void *closure) |
193 | 0 | { |
194 | 0 | ffi_call_int (cif, fn, rvalue, avalue, closure); |
195 | 0 | } |
196 | | |
197 | | |
198 | | extern void ffi_closure_win64(void) FFI_HIDDEN; |
199 | | extern void ffi_go_closure_win64(void) FFI_HIDDEN; |
200 | | |
201 | | ffi_status |
202 | | EFI64(ffi_prep_closure_loc)(ffi_closure* closure, |
203 | | ffi_cif* cif, |
204 | | void (*fun)(ffi_cif*, void*, void**, void*), |
205 | | void *user_data, |
206 | | void *codeloc) |
207 | 0 | { |
208 | 0 | static const unsigned char trampoline[16] = { |
209 | | /* leaq -0x7(%rip),%r10 # 0x0 */ |
210 | 0 | 0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff, |
211 | | /* jmpq *0x3(%rip) # 0x10 */ |
212 | 0 | 0xff, 0x25, 0x03, 0x00, 0x00, 0x00, |
213 | | /* nopl (%rax) */ |
214 | 0 | 0x0f, 0x1f, 0x00 |
215 | 0 | }; |
216 | 0 | char *tramp = closure->tramp; |
217 | |
|
218 | 0 | switch (cif->abi) |
219 | 0 | { |
220 | 0 | case FFI_WIN64: |
221 | 0 | case FFI_GNUW64: |
222 | 0 | break; |
223 | 0 | default: |
224 | 0 | return FFI_BAD_ABI; |
225 | 0 | } |
226 | | |
227 | 0 | memcpy (tramp, trampoline, sizeof(trampoline)); |
228 | 0 | *(UINT64 *)(tramp + 16) = (uintptr_t)ffi_closure_win64; |
229 | |
|
230 | 0 | closure->cif = cif; |
231 | 0 | closure->fun = fun; |
232 | 0 | closure->user_data = user_data; |
233 | |
|
234 | 0 | return FFI_OK; |
235 | 0 | } |
236 | | |
237 | | ffi_status |
238 | | EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif, |
239 | | void (*fun)(ffi_cif*, void*, void**, void*)) |
240 | 0 | { |
241 | 0 | switch (cif->abi) |
242 | 0 | { |
243 | 0 | case FFI_WIN64: |
244 | 0 | case FFI_GNUW64: |
245 | 0 | break; |
246 | 0 | default: |
247 | 0 | return FFI_BAD_ABI; |
248 | 0 | } |
249 | | |
250 | 0 | closure->tramp = ffi_go_closure_win64; |
251 | 0 | closure->cif = cif; |
252 | 0 | closure->fun = fun; |
253 | |
|
254 | 0 | return FFI_OK; |
255 | 0 | } |
256 | | |
257 | | struct win64_closure_frame |
258 | | { |
259 | | UINT64 rvalue[2]; |
260 | | UINT64 fargs[4]; |
261 | | UINT64 retaddr; |
262 | | UINT64 args[]; |
263 | | }; |
264 | | |
265 | | /* Force the inner function to use the MS ABI. When compiling on win64 |
266 | | this is a nop. When compiling on unix, this simplifies the assembly, |
267 | | and places the burden of saving the extra call-saved registers on |
268 | | the compiler. */ |
269 | | int FFI_HIDDEN __attribute__((ms_abi)) |
270 | | ffi_closure_win64_inner(ffi_cif *cif, |
271 | | void (*fun)(ffi_cif*, void*, void**, void*), |
272 | | void *user_data, |
273 | | struct win64_closure_frame *frame) |
274 | 0 | { |
275 | 0 | void **avalue; |
276 | 0 | void *rvalue; |
277 | 0 | int i, n, nreg, flags; |
278 | |
|
279 | 0 | avalue = alloca(cif->nargs * sizeof(void *)); |
280 | 0 | rvalue = frame->rvalue; |
281 | 0 | nreg = 0; |
282 | | |
283 | | /* When returning a structure, the address is in the first argument. |
284 | | We must also be prepared to return the same address in eax, so |
285 | | install that address in the frame and pretend we return a pointer. */ |
286 | 0 | flags = cif->flags; |
287 | 0 | if (flags == FFI_TYPE_STRUCT) |
288 | 0 | { |
289 | 0 | rvalue = (void *)(uintptr_t)frame->args[0]; |
290 | 0 | frame->rvalue[0] = frame->args[0]; |
291 | 0 | nreg = 1; |
292 | 0 | } |
293 | |
|
294 | 0 | for (i = 0, n = cif->nargs; i < n; ++i, ++nreg) |
295 | 0 | { |
296 | 0 | size_t size = cif->arg_types[i]->size; |
297 | 0 | size_t type = cif->arg_types[i]->type; |
298 | 0 | void *a; |
299 | |
|
300 | 0 | if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT) |
301 | 0 | { |
302 | 0 | if (nreg < 4) |
303 | 0 | a = &frame->fargs[nreg]; |
304 | 0 | else |
305 | 0 | a = &frame->args[nreg]; |
306 | 0 | } |
307 | 0 | else if (size == 1 || size == 2 || size == 4 || size == 8) |
308 | 0 | a = &frame->args[nreg]; |
309 | 0 | else |
310 | 0 | a = (void *)(uintptr_t)frame->args[nreg]; |
311 | |
|
312 | 0 | avalue[i] = a; |
313 | 0 | } |
314 | | |
315 | | /* Invoke the closure. */ |
316 | 0 | fun (cif, rvalue, avalue, user_data); |
317 | 0 | return flags; |
318 | 0 | } |