Static tramp v5 (#624)
[platform/upstream/libffi.git] / src / x86 / ffiw64.c
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 #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>
34
35 #ifdef X86_WIN64
36 #define EFI64(name) name
37 #else
38 #define EFI64(name) FFI_HIDDEN name##_efi64
39 #endif
40
41 struct win64_call_frame
42 {
43   UINT64 rbp;           /* 0 */
44   UINT64 retaddr;       /* 8 */
45   UINT64 fn;            /* 16 */
46   UINT64 flags;         /* 24 */
47   UINT64 rvalue;        /* 32 */
48 };
49
50 extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
51                             void *closure) FFI_HIDDEN;
52
53 ffi_status FFI_HIDDEN
54 EFI64(ffi_prep_cif_machdep)(ffi_cif *cif)
55 {
56   int flags, n;
57
58   switch (cif->abi)
59     {
60     case FFI_WIN64:
61     case FFI_GNUW64:
62       break;
63     default:
64       return FFI_BAD_ABI;
65     }
66
67   flags = cif->rtype->type;
68   switch (flags)
69     {
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)
82         {
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;
95         }
96       break;
97     }
98   cif->flags = flags;
99
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;
107
108   return FFI_OK;
109 }
110
111 static void
112 ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
113               void **avalue, void *closure)
114 {
115   int i, j, n, flags;
116   UINT64 *stack;
117   size_t rsize;
118   struct win64_call_frame *frame;
119
120   FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
121
122   flags = cif->flags;
123   rsize = 0;
124
125   /* If we have no return value for a structure, we need to create one.
126      Otherwise we can ignore the return type entirely.  */
127   if (rvalue == NULL)
128     {
129       if (flags == FFI_TYPE_STRUCT)
130         rsize = cif->rtype->size;
131       else
132         flags = FFI_TYPE_VOID;
133     }
134
135   stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
136   frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
137   if (rsize)
138     rvalue = frame + 1;
139
140   frame->fn = (uintptr_t)fn;
141   frame->flags = flags;
142   frame->rvalue = (uintptr_t)rvalue;
143
144   j = 0;
145   if (flags == FFI_TYPE_STRUCT)
146     {
147       stack[0] = (uintptr_t)rvalue;
148       j = 1;
149     }
150
151   for (i = 0, n = cif->nargs; i < n; ++i, ++j)
152     {
153       switch (cif->arg_types[i]->size)
154         {
155         case 8:
156           stack[j] = *(UINT64 *)avalue[i];
157           break;
158         case 4:
159           stack[j] = *(UINT32 *)avalue[i];
160           break;
161         case 2:
162           stack[j] = *(UINT16 *)avalue[i];
163           break;
164         case 1:
165           stack[j] = *(UINT8 *)avalue[i];
166           break;
167         default:
168           stack[j] = (uintptr_t)avalue[i];
169           break;
170         }
171     }
172
173   ffi_call_win64 (stack, frame, closure);
174 }
175
176 void
177 EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
178 {
179   ffi_call_int (cif, fn, rvalue, avalue, NULL);
180 }
181
182 void
183 EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue,
184              void **avalue, void *closure)
185 {
186   ffi_call_int (cif, fn, rvalue, avalue, closure);
187 }
188
189
190 extern void ffi_closure_win64(void) FFI_HIDDEN;
191 #if defined(FFI_EXEC_STATIC_TRAMP)
192 extern void ffi_closure_win64_alt(void) FFI_HIDDEN;
193 #endif
194
195 #ifdef FFI_GO_CLOSURES
196 extern void ffi_go_closure_win64(void) FFI_HIDDEN;
197 #endif
198
199 ffi_status
200 EFI64(ffi_prep_closure_loc)(ffi_closure* closure,
201                       ffi_cif* cif,
202                       void (*fun)(ffi_cif*, void*, void**, void*),
203                       void *user_data,
204                       void *codeloc)
205 {
206   static const unsigned char trampoline[FFI_TRAMPOLINE_SIZE - 8] = {
207     /* endbr64 */
208     0xf3, 0x0f, 0x1e, 0xfa,
209     /* leaq  -0xb(%rip),%r10   # 0x0  */
210     0x4c, 0x8d, 0x15, 0xf5, 0xff, 0xff, 0xff,
211     /* jmpq  *0x7(%rip)        # 0x18 */
212     0xff, 0x25, 0x07, 0x00, 0x00, 0x00,
213     /* nopl  0(%rax) */
214     0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00
215   };
216   char *tramp = closure->tramp;
217
218   switch (cif->abi)
219     {
220     case FFI_WIN64:
221     case FFI_GNUW64:
222       break;
223     default:
224       return FFI_BAD_ABI;
225     }
226
227 #if defined(FFI_EXEC_STATIC_TRAMP)
228   if (ffi_tramp_is_present(closure))
229     {
230       /* Initialize the static trampoline's parameters. */
231       ffi_tramp_set_parms (closure->ftramp, ffi_closure_win64_alt, closure);
232       goto out;
233     }
234 #endif
235
236   /* Initialize the dynamic trampoline. */
237   memcpy (tramp, trampoline, sizeof(trampoline));
238   *(UINT64 *)(tramp + sizeof (trampoline)) = (uintptr_t)ffi_closure_win64;
239
240 out:
241   closure->cif = cif;
242   closure->fun = fun;
243   closure->user_data = user_data;
244
245   return FFI_OK;
246 }
247
248 #ifdef FFI_GO_CLOSURES
249 ffi_status
250 EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif,
251                      void (*fun)(ffi_cif*, void*, void**, void*))
252 {
253   switch (cif->abi)
254     {
255     case FFI_WIN64:
256     case FFI_GNUW64:
257       break;
258     default:
259       return FFI_BAD_ABI;
260     }
261
262   closure->tramp = ffi_go_closure_win64;
263   closure->cif = cif;
264   closure->fun = fun;
265
266   return FFI_OK;
267 }
268 #endif
269
270 struct win64_closure_frame
271 {
272   UINT64 rvalue[2];
273   UINT64 fargs[4];
274   UINT64 retaddr;
275   UINT64 args[];
276 };
277
278 /* Force the inner function to use the MS ABI.  When compiling on win64
279    this is a nop.  When compiling on unix, this simplifies the assembly,
280    and places the burden of saving the extra call-saved registers on
281    the compiler.  */
282 int FFI_HIDDEN __attribute__((ms_abi))
283 ffi_closure_win64_inner(ffi_cif *cif,
284                         void (*fun)(ffi_cif*, void*, void**, void*),
285                         void *user_data,
286                         struct win64_closure_frame *frame)
287 {
288   void **avalue;
289   void *rvalue;
290   int i, n, nreg, flags;
291
292   avalue = alloca(cif->nargs * sizeof(void *));
293   rvalue = frame->rvalue;
294   nreg = 0;
295
296   /* When returning a structure, the address is in the first argument.
297      We must also be prepared to return the same address in eax, so
298      install that address in the frame and pretend we return a pointer.  */
299   flags = cif->flags;
300   if (flags == FFI_TYPE_STRUCT)
301     {
302       rvalue = (void *)(uintptr_t)frame->args[0];
303       frame->rvalue[0] = frame->args[0];
304       nreg = 1;
305     }
306
307   for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
308     {
309       size_t size = cif->arg_types[i]->size;
310       size_t type = cif->arg_types[i]->type;
311       void *a;
312
313       if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
314         {
315           if (nreg < 4)
316             a = &frame->fargs[nreg];
317           else
318             a = &frame->args[nreg];
319         }
320       else if (size == 1 || size == 2 || size == 4 || size == 8)
321         a = &frame->args[nreg];
322       else
323         a = (void *)(uintptr_t)frame->args[nreg];
324
325       avalue[i] = a;
326     }
327
328   /* Invoke the closure.  */
329   fun (cif, rvalue, avalue, user_data);
330   return flags;
331 }
332
333 #endif /* __x86_64__ */