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