• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
EFI64(ffi_prep_cif_machdep)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
ffi_call_int(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue,void * closure)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
EFI64(ffi_call)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
EFI64(ffi_call_go)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
EFI64(ffi_prep_closure_loc)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
EFI64(ffi_prep_go_closure)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))
ffi_closure_win64_inner(ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,struct win64_closure_frame * frame)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__ */
312