Daily bump.
[gcc.git] / libffi / src / x86 / ffiw64.c
blob6870d07c92b787c26544ff5e69c8bd4febe3e71c
1 /* -----------------------------------------------------------------------
2 ffiw64.c - Copyright (c) 2018 Anthony Green
3 Copyright (c) 2014 Red Hat, Inc.
5 x86 win64 Foreign Function Interface
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:
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
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 ----------------------------------------------------------------------- */
28 #if defined(__x86_64__) || defined(_M_AMD64)
29 #include <ffi.h>
30 #include <ffi_common.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <tramp.h>
35 #ifdef X86_WIN64
36 #define EFI64(name) name
37 #else
38 #define EFI64(name) FFI_HIDDEN name##_efi64
39 #endif
41 struct win64_call_frame
43 UINT64 rbp; /* 0 */
44 UINT64 retaddr; /* 8 */
45 UINT64 fn; /* 16 */
46 UINT64 flags; /* 24 */
47 UINT64 rvalue; /* 32 */
50 extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
51 void *closure) FFI_HIDDEN;
53 ffi_status FFI_HIDDEN
54 EFI64(ffi_prep_cif_machdep)(ffi_cif *cif)
56 int flags, n;
58 switch (cif->abi)
60 case FFI_WIN64:
61 case FFI_GNUW64:
62 break;
63 default:
64 return FFI_BAD_ABI;
67 flags = cif->rtype->type;
68 switch (flags)
70 default:
71 break;
72 case FFI_TYPE_LONGDOUBLE:
73 /* GCC returns long double values by reference, like a struct */
74 if (cif->abi == FFI_GNUW64)
75 flags = FFI_TYPE_STRUCT;
76 break;
77 case FFI_TYPE_COMPLEX:
78 flags = FFI_TYPE_STRUCT;
79 /* FALLTHRU */
80 case FFI_TYPE_STRUCT:
81 switch (cif->rtype->size)
83 case 8:
84 flags = FFI_TYPE_UINT64;
85 break;
86 case 4:
87 flags = FFI_TYPE_SMALL_STRUCT_4B;
88 break;
89 case 2:
90 flags = FFI_TYPE_SMALL_STRUCT_2B;
91 break;
92 case 1:
93 flags = FFI_TYPE_SMALL_STRUCT_1B;
94 break;
96 break;
98 cif->flags = flags;
100 /* Each argument either fits in a register, an 8 byte slot, or is
101 passed by reference with the pointer in the 8 byte slot. */
102 n = cif->nargs;
103 n += (flags == FFI_TYPE_STRUCT);
104 if (n < 4)
105 n = 4;
106 cif->bytes = n * 8;
108 return FFI_OK;
111 /* We perform some black magic here to use some of the parent's stack frame in
112 * ffi_call_win64() that breaks with the MSVC compiler with the /RTCs or /GZ
113 * flags. Disable the 'Stack frame run time error checking' for this function
114 * so we don't hit weird exceptions in debug builds. */
115 #if defined(_MSC_VER)
116 #pragma runtime_checks("s", off)
117 #endif
118 static void
119 ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
120 void **avalue, void *closure)
122 int i, j, n, flags;
123 UINT64 *stack;
124 size_t rsize;
125 struct win64_call_frame *frame;
127 FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
129 flags = cif->flags;
130 rsize = 0;
132 /* If we have no return value for a structure, we need to create one.
133 Otherwise we can ignore the return type entirely. */
134 if (rvalue == NULL)
136 if (flags == FFI_TYPE_STRUCT)
137 rsize = cif->rtype->size;
138 else
139 flags = FFI_TYPE_VOID;
142 stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
143 frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
144 if (rsize)
145 rvalue = frame + 1;
147 frame->fn = (uintptr_t)fn;
148 frame->flags = flags;
149 frame->rvalue = (uintptr_t)rvalue;
151 j = 0;
152 if (flags == FFI_TYPE_STRUCT)
154 stack[0] = (uintptr_t)rvalue;
155 j = 1;
158 for (i = 0, n = cif->nargs; i < n; ++i, ++j)
160 switch (cif->arg_types[i]->size)
162 case 8:
163 stack[j] = *(UINT64 *)avalue[i];
164 break;
165 case 4:
166 stack[j] = *(UINT32 *)avalue[i];
167 break;
168 case 2:
169 stack[j] = *(UINT16 *)avalue[i];
170 break;
171 case 1:
172 stack[j] = *(UINT8 *)avalue[i];
173 break;
174 default:
175 stack[j] = (uintptr_t)avalue[i];
176 break;
180 ffi_call_win64 (stack, frame, closure);
182 #if defined(_MSC_VER)
183 #pragma runtime_checks("s", restore)
184 #endif
186 void
187 EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
189 ffi_call_int (cif, fn, rvalue, avalue, NULL);
192 void
193 EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue,
194 void **avalue, void *closure)
196 ffi_call_int (cif, fn, rvalue, avalue, closure);
200 extern void ffi_closure_win64(void) FFI_HIDDEN;
201 #if defined(FFI_EXEC_STATIC_TRAMP)
202 extern void ffi_closure_win64_alt(void) FFI_HIDDEN;
203 #endif
205 #ifdef FFI_GO_CLOSURES
206 extern void ffi_go_closure_win64(void) FFI_HIDDEN;
207 #endif
209 ffi_status
210 EFI64(ffi_prep_closure_loc)(ffi_closure* closure,
211 ffi_cif* cif,
212 void (*fun)(ffi_cif*, void*, void**, void*),
213 void *user_data,
214 void *codeloc)
216 static const unsigned char trampoline[FFI_TRAMPOLINE_SIZE - 8] = {
217 /* endbr64 */
218 0xf3, 0x0f, 0x1e, 0xfa,
219 /* leaq -0xb(%rip),%r10 # 0x0 */
220 0x4c, 0x8d, 0x15, 0xf5, 0xff, 0xff, 0xff,
221 /* jmpq *0x7(%rip) # 0x18 */
222 0xff, 0x25, 0x07, 0x00, 0x00, 0x00,
223 /* nopl 0(%rax) */
224 0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00
226 char *tramp = closure->tramp;
228 switch (cif->abi)
230 case FFI_WIN64:
231 case FFI_GNUW64:
232 break;
233 default:
234 return FFI_BAD_ABI;
237 #if defined(FFI_EXEC_STATIC_TRAMP)
238 if (ffi_tramp_is_present(closure))
240 /* Initialize the static trampoline's parameters. */
241 ffi_tramp_set_parms (closure->ftramp, ffi_closure_win64_alt, closure);
242 goto out;
244 #endif
246 /* Initialize the dynamic trampoline. */
247 memcpy (tramp, trampoline, sizeof(trampoline));
248 *(UINT64 *)(tramp + sizeof (trampoline)) = (uintptr_t)ffi_closure_win64;
250 out:
251 closure->cif = cif;
252 closure->fun = fun;
253 closure->user_data = user_data;
255 return FFI_OK;
258 #ifdef FFI_GO_CLOSURES
259 ffi_status
260 EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif,
261 void (*fun)(ffi_cif*, void*, void**, void*))
263 switch (cif->abi)
265 case FFI_WIN64:
266 case FFI_GNUW64:
267 break;
268 default:
269 return FFI_BAD_ABI;
272 closure->tramp = ffi_go_closure_win64;
273 closure->cif = cif;
274 closure->fun = fun;
276 return FFI_OK;
278 #endif
280 struct win64_closure_frame
282 UINT64 rvalue[2];
283 UINT64 fargs[4];
284 UINT64 retaddr;
285 UINT64 args[];
288 /* Force the inner function to use the MS ABI. When compiling on win64
289 this is a nop. When compiling on unix, this simplifies the assembly,
290 and places the burden of saving the extra call-saved registers on
291 the compiler. */
292 int FFI_HIDDEN __attribute__((ms_abi))
293 ffi_closure_win64_inner(ffi_cif *cif,
294 void (*fun)(ffi_cif*, void*, void**, void*),
295 void *user_data,
296 struct win64_closure_frame *frame)
298 void **avalue;
299 void *rvalue;
300 int i, n, nreg, flags;
302 avalue = alloca(cif->nargs * sizeof(void *));
303 rvalue = frame->rvalue;
304 nreg = 0;
306 /* When returning a structure, the address is in the first argument.
307 We must also be prepared to return the same address in eax, so
308 install that address in the frame and pretend we return a pointer. */
309 flags = cif->flags;
310 if (flags == FFI_TYPE_STRUCT)
312 rvalue = (void *)(uintptr_t)frame->args[0];
313 frame->rvalue[0] = frame->args[0];
314 nreg = 1;
317 for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
319 size_t size = cif->arg_types[i]->size;
320 size_t type = cif->arg_types[i]->type;
321 void *a;
323 if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
325 if (nreg < 4)
326 a = &frame->fargs[nreg];
327 else
328 a = &frame->args[nreg];
330 else if (size == 1 || size == 2 || size == 4 || size == 8)
331 a = &frame->args[nreg];
332 else
333 a = (void *)(uintptr_t)frame->args[nreg];
335 avalue[i] = a;
338 /* Invoke the closure. */
339 fun (cif, rvalue, avalue, user_data);
340 return flags;
343 #endif /* __x86_64__ */