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 
 34 #ifdef X86_WIN64
 35 #define EFI64(name) name
 36 #else
 37 #define EFI64(name) FFI_HIDDEN name##_efi64
 38 #endif
 39 
 40 struct win64_call_frame
 41 {
 42   UINT64 rbp;       /* 0 */
 43   UINT64 retaddr;   /* 8 */
 44   UINT64 fn;        /* 16 */
 45   UINT64 flags;     /* 24 */
 46   UINT64 rvalue;    /* 32 */
 47 };
 48 
 49 extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
 50                 void *closure) FFI_HIDDEN;
 51 
 52 ffi_status FFI_HIDDEN
 53 EFI64(ffi_prep_cif_machdep)(ffi_cif *cif)
 54 {
 55   int flags, n;
 56 
 57   switch (cif->abi)
 58     {
 59     case FFI_WIN64:
 60     case FFI_GNUW64:
 61       break;
 62     default:
 63       return FFI_BAD_ABI;
 64     }
 65 
 66   flags = cif->rtype->type;
 67   switch (flags)
 68     {
 69     default:
 70       break;
 71     case FFI_TYPE_LONGDOUBLE:
 72       /* GCC returns long double values by reference, like a struct */
 73       if (cif->abi == FFI_GNUW64)
 74     flags = FFI_TYPE_STRUCT;
 75       break;
 76     case FFI_TYPE_COMPLEX:
 77       flags = FFI_TYPE_STRUCT;
 78       /* FALLTHRU */
 79     case FFI_TYPE_STRUCT:
 80       switch (cif->rtype->size)
 81     {
 82     case 8:
 83       flags = FFI_TYPE_UINT64;
 84       break;
 85     case 4:
 86       flags = FFI_TYPE_SMALL_STRUCT_4B;
 87       break;
 88     case 2:
 89       flags = FFI_TYPE_SMALL_STRUCT_2B;
 90       break;
 91     case 1:
 92       flags = FFI_TYPE_SMALL_STRUCT_1B;
 93       break;
 94     }
 95       break;
 96     }
 97   cif->flags = flags;
 98 
 99   /* Each argument either fits in a register, an 8 byte slot, or is
100      passed by reference with the pointer in the 8 byte slot.  */
101   n = cif->nargs;
102   n += (flags == FFI_TYPE_STRUCT);
103   if (n < 4)
104     n = 4;
105   cif->bytes = n * 8;
106 
107   return FFI_OK;
108 }
109 
110 static void
111 ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
112           void **avalue, void *closure)
113 {
114   int i, j, n, flags;
115   UINT64 *stack;
116   size_t rsize;
117   struct win64_call_frame *frame;
118 
119   FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64);
120 
121   flags = cif->flags;
122   rsize = 0;
123 
124   /* If we have no return value for a structure, we need to create one.
125      Otherwise we can ignore the return type entirely.  */
126   if (rvalue == NULL)
127     {
128       if (flags == FFI_TYPE_STRUCT)
129     rsize = cif->rtype->size;
130       else
131     flags = FFI_TYPE_VOID;
132     }
133 
134   stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
135   frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
136   if (rsize)
137     rvalue = frame + 1;
138 
139   frame->fn = (uintptr_t)fn;
140   frame->flags = flags;
141   frame->rvalue = (uintptr_t)rvalue;
142 
143   j = 0;
144   if (flags == FFI_TYPE_STRUCT)
145     {
146       stack[0] = (uintptr_t)rvalue;
147       j = 1;
148     }
149 
150   for (i = 0, n = cif->nargs; i < n; ++i, ++j)
151     {
152       switch (cif->arg_types[i]->size)
153     {
154     case 8:
155       stack[j] = *(UINT64 *)avalue[i];
156       break;
157     case 4:
158       stack[j] = *(UINT32 *)avalue[i];
159       break;
160     case 2:
161       stack[j] = *(UINT16 *)avalue[i];
162       break;
163     case 1:
164       stack[j] = *(UINT8 *)avalue[i];
165       break;
166     default:
167       stack[j] = (uintptr_t)avalue[i];
168       break;
169     }
170     }
171 
172   ffi_call_win64 (stack, frame, closure);
173 }
174 
175 void
176 EFI64(ffi_call)(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
177 {
178   ffi_call_int (cif, fn, rvalue, avalue, NULL);
179 }
180 
181 void
182 EFI64(ffi_call_go)(ffi_cif *cif, void (*fn)(void), void *rvalue,
183          void **avalue, void *closure)
184 {
185   ffi_call_int (cif, fn, rvalue, avalue, closure);
186 }
187 
188 
189 extern void ffi_closure_win64(void) FFI_HIDDEN;
190 extern void ffi_go_closure_win64(void) FFI_HIDDEN;
191 
192 ffi_status
193 EFI64(ffi_prep_closure_loc)(ffi_closure* closure,
194               ffi_cif* cif,
195               void (*fun)(ffi_cif*, void*, void**, void*),
196               void *user_data,
197               void *codeloc)
198 {
199   static const unsigned char trampoline[16] = {
200     /* leaq  -0x7(%rip),%r10   # 0x0  */
201     0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
202     /* jmpq  *0x3(%rip)        # 0x10 */
203     0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
204     /* nopl  (%rax) */
205     0x0f, 0x1f, 0x00
206   };
207   char *tramp = closure->tramp;
208 
209   switch (cif->abi)
210     {
211     case FFI_WIN64:
212     case FFI_GNUW64:
213       break;
214     default:
215       return FFI_BAD_ABI;
216     }
217 
218   memcpy (tramp, trampoline, sizeof(trampoline));
219   *(UINT64 *)(tramp + 16) = (uintptr_t)ffi_closure_win64;
220 
221   closure->cif = cif;
222   closure->fun = fun;
223   closure->user_data = user_data;
224 
225   return FFI_OK;
226 }
227 
228 ffi_status
229 EFI64(ffi_prep_go_closure)(ffi_go_closure* closure, ffi_cif* cif,
230              void (*fun)(ffi_cif*, void*, void**, void*))
231 {
232   switch (cif->abi)
233     {
234     case FFI_WIN64:
235     case FFI_GNUW64:
236       break;
237     default:
238       return FFI_BAD_ABI;
239     }
240 
241   closure->tramp = ffi_go_closure_win64;
242   closure->cif = cif;
243   closure->fun = fun;
244 
245   return FFI_OK;
246 }
247 
248 struct win64_closure_frame
249 {
250   UINT64 rvalue[2];
251   UINT64 fargs[4];
252   UINT64 retaddr;
253   UINT64 args[];
254 };
255 
256 /* Force the inner function to use the MS ABI.  When compiling on win64
257    this is a nop.  When compiling on unix, this simplifies the assembly,
258    and places the burden of saving the extra call-saved registers on
259    the compiler.  */
260 int FFI_HIDDEN __attribute__((ms_abi))
261 ffi_closure_win64_inner(ffi_cif *cif,
262             void (*fun)(ffi_cif*, void*, void**, void*),
263             void *user_data,
264             struct win64_closure_frame *frame)
265 {
266   void **avalue;
267   void *rvalue;
268   int i, n, nreg, flags;
269 
270   avalue = alloca(cif->nargs * sizeof(void *));
271   rvalue = frame->rvalue;
272   nreg = 0;
273 
274   /* When returning a structure, the address is in the first argument.
275      We must also be prepared to return the same address in eax, so
276      install that address in the frame and pretend we return a pointer.  */
277   flags = cif->flags;
278   if (flags == FFI_TYPE_STRUCT)
279     {
280       rvalue = (void *)(uintptr_t)frame->args[0];
281       frame->rvalue[0] = frame->args[0];
282       nreg = 1;
283     }
284 
285   for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
286     {
287       size_t size = cif->arg_types[i]->size;
288       size_t type = cif->arg_types[i]->type;
289       void *a;
290 
291       if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
292     {
293       if (nreg < 4)
294         a = &frame->fargs[nreg];
295       else
296         a = &frame->args[nreg];
297     }
298       else if (size == 1 || size == 2 || size == 4 || size == 8)
299     a = &frame->args[nreg];
300       else
301     a = (void *)(uintptr_t)frame->args[nreg];
302 
303       avalue[i] = a;
304     }
305 
306   /* Invoke the closure.  */
307   fun (cif, rvalue, avalue, user_data);
308   return flags;
309 }
310 
311 #endif /* __x86_64__ */