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)
30 #include <ffi_common.h>
36 #define EFI64(name) name
38 #define EFI64(name) FFI_HIDDEN name##_efi64
41 struct win64_call_frame
44 UINT64 retaddr
; /* 8 */
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
;
54 EFI64(ffi_prep_cif_machdep
)(ffi_cif
*cif
)
67 flags
= cif
->rtype
->type
;
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
;
77 case FFI_TYPE_COMPLEX
:
78 flags
= FFI_TYPE_STRUCT
;
81 switch (cif
->rtype
->size
)
84 flags
= FFI_TYPE_UINT64
;
87 flags
= FFI_TYPE_SMALL_STRUCT_4B
;
90 flags
= FFI_TYPE_SMALL_STRUCT_2B
;
93 flags
= FFI_TYPE_SMALL_STRUCT_1B
;
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. */
103 n
+= (flags
== FFI_TYPE_STRUCT
);
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)
119 ffi_call_int (ffi_cif
*cif
, void (*fn
)(void), void *rvalue
,
120 void **avalue
, void *closure
)
125 struct win64_call_frame
*frame
;
127 FFI_ASSERT(cif
->abi
== FFI_GNUW64
|| cif
->abi
== FFI_WIN64
);
132 /* If we have no return value for a structure, we need to create one.
133 Otherwise we can ignore the return type entirely. */
136 if (flags
== FFI_TYPE_STRUCT
)
137 rsize
= cif
->rtype
->size
;
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
);
147 frame
->fn
= (uintptr_t)fn
;
148 frame
->flags
= flags
;
149 frame
->rvalue
= (uintptr_t)rvalue
;
152 if (flags
== FFI_TYPE_STRUCT
)
154 stack
[0] = (uintptr_t)rvalue
;
158 for (i
= 0, n
= cif
->nargs
; i
< n
; ++i
, ++j
)
160 switch (cif
->arg_types
[i
]->size
)
163 stack
[j
] = *(UINT64
*)avalue
[i
];
166 stack
[j
] = *(UINT32
*)avalue
[i
];
169 stack
[j
] = *(UINT16
*)avalue
[i
];
172 stack
[j
] = *(UINT8
*)avalue
[i
];
175 stack
[j
] = (uintptr_t)avalue
[i
];
180 ffi_call_win64 (stack
, frame
, closure
);
182 #if defined(_MSC_VER)
183 #pragma runtime_checks("s", restore)
187 EFI64(ffi_call
)(ffi_cif
*cif
, void (*fn
)(void), void *rvalue
, void **avalue
)
189 ffi_call_int (cif
, fn
, rvalue
, avalue
, NULL
);
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
;
205 #ifdef FFI_GO_CLOSURES
206 extern void ffi_go_closure_win64(void) FFI_HIDDEN
;
210 EFI64(ffi_prep_closure_loc
)(ffi_closure
* closure
,
212 void (*fun
)(ffi_cif
*, void*, void**, void*),
216 static const unsigned char trampoline
[FFI_TRAMPOLINE_SIZE
- 8] = {
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,
224 0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00
226 char *tramp
= closure
->tramp
;
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
);
246 /* Initialize the dynamic trampoline. */
247 memcpy (tramp
, trampoline
, sizeof(trampoline
));
248 *(UINT64
*)(tramp
+ sizeof (trampoline
)) = (uintptr_t)ffi_closure_win64
;
253 closure
->user_data
= user_data
;
258 #ifdef FFI_GO_CLOSURES
260 EFI64(ffi_prep_go_closure
)(ffi_go_closure
* closure
, ffi_cif
* cif
,
261 void (*fun
)(ffi_cif
*, void*, void**, void*))
272 closure
->tramp
= ffi_go_closure_win64
;
280 struct win64_closure_frame
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
292 int FFI_HIDDEN
__attribute__((ms_abi
))
293 ffi_closure_win64_inner(ffi_cif
*cif
,
294 void (*fun
)(ffi_cif
*, void*, void**, void*),
296 struct win64_closure_frame
*frame
)
300 int i
, n
, nreg
, flags
;
302 avalue
= alloca(cif
->nargs
* sizeof(void *));
303 rvalue
= frame
->rvalue
;
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. */
310 if (flags
== FFI_TYPE_STRUCT
)
312 rvalue
= (void *)(uintptr_t)frame
->args
[0];
313 frame
->rvalue
[0] = frame
->args
[0];
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
;
323 if (type
== FFI_TYPE_DOUBLE
|| type
== FFI_TYPE_FLOAT
)
326 a
= &frame
->fargs
[nreg
];
328 a
= &frame
->args
[nreg
];
330 else if (size
== 1 || size
== 2 || size
== 4 || size
== 8)
331 a
= &frame
->args
[nreg
];
333 a
= (void *)(uintptr_t)frame
->args
[nreg
];
338 /* Invoke the closure. */
339 fun (cif
, rvalue
, avalue
, user_data
);
343 #endif /* __x86_64__ */