fix calling convention and arg types
[m6w6/ext-psi] / src / libffi.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include "context.h"
28 #include "call.h"
29 #include "php.h"
30
31 #ifdef HAVE_LIBFFI
32
33 #include "libffi_compat.h"
34
35 #if HAVE_INT128
36 static ffi_type *ffi_type_sint128;
37 static ffi_type *ffi_type_uint128;
38 #endif
39
40 struct psi_ffi_context {
41 ffi_cif signature;
42 ffi_type *params[2];
43 };
44
45 struct psi_ffi_callback_info {
46 struct psi_ffi_impl_info *impl_info;
47 struct psi_let_exp *let_exp;
48
49 void *code;
50 ffi_closure *closure;
51 };
52
53 struct psi_ffi_decl_info {
54 ffi_cif signature;
55 struct psi_ffi_struct_info *rv_array;
56 ffi_type *params[1];
57 };
58
59 struct psi_ffi_extvar_info {
60 struct {
61 ffi_cif signature;
62 void *code;
63 ffi_closure *closure;
64 } get;
65 struct {
66 ffi_cif signature;
67 ffi_type *params[1];
68 void *code;
69 ffi_closure *closure;
70 } set;
71 };
72
73 struct psi_ffi_impl_info {
74 struct psi_context *context;
75 struct psi_call_frame *frame;
76
77 void *code;
78 ffi_closure *closure;
79 };
80
81 struct psi_ffi_struct_info {
82 ffi_type strct;
83 struct psi_plist *eles;
84 };
85
86 static inline ffi_type *psi_ffi_token_type(token_t t)
87 {
88 switch (t) {
89 default:
90 assert(0);
91 /* no break */
92 case PSI_T_VOID:
93 return &ffi_type_void;
94 case PSI_T_INT8:
95 return &ffi_type_sint8;
96 case PSI_T_UINT8:
97 return &ffi_type_uint8;
98 case PSI_T_INT16:
99 return &ffi_type_sint16;
100 case PSI_T_UINT16:
101 return &ffi_type_uint16;
102 case PSI_T_INT32:
103 return &ffi_type_sint32;
104 case PSI_T_UINT32:
105 return &ffi_type_uint32;
106 case PSI_T_INT64:
107 return &ffi_type_sint64;
108 case PSI_T_UINT64:
109 return &ffi_type_uint64;
110 #if HAVE_INT128
111 case PSI_T_INT128:
112 return ffi_type_sint128;
113 case PSI_T_UINT128:
114 return ffi_type_uint128;
115 #endif
116 case PSI_T_BOOL:
117 return &ffi_type_uchar;
118 case PSI_T_ENUM:
119 return &ffi_type_sint;
120 case PSI_T_FLOAT:
121 return &ffi_type_float;
122 case PSI_T_DOUBLE:
123 return &ffi_type_double;
124 #ifdef HAVE_LONG_DOUBLE
125 case PSI_T_LONG_DOUBLE:
126 return &ffi_type_longdouble;
127 #endif
128 case PSI_T_POINTER:
129 case PSI_T_FUNCTION:
130 return &ffi_type_pointer;
131 }
132 }
133 static inline ffi_type *psi_ffi_impl_type(token_t impl_type)
134 {
135 switch (impl_type) {
136 case PSI_T_BOOL:
137 return &ffi_type_sint8;
138 case PSI_T_INT:
139 return &ffi_type_sint64;
140 case PSI_T_STRING:
141 return &ffi_type_pointer;
142 case PSI_T_FLOAT:
143 case PSI_T_DOUBLE:
144 return &ffi_type_double;
145 EMPTY_SWITCH_DEFAULT_CASE();
146 }
147 return NULL;
148 }
149
150 static void psi_ffi_type_free(ffi_type **typ_ptr)
151 {
152 pefree(*typ_ptr, 1);
153 }
154
155 static inline ffi_abi psi_ffi_abi(zend_string *convention) {
156 if (FFI_LAST_ABI - 2 != FFI_FIRST_ABI) {
157 #ifdef HAVE_FFI_STDCALL
158 if (zend_string_equals_literal(convention, "stdcall")) {
159 return FFI_STDCALL;
160 }
161 #endif
162 #ifdef HAVE_FFI_FASTCALL
163 if (zend_string_equals_literal(convention, "fastcall")) {
164 return FFI_FASTCALL;
165 }
166 #endif
167 }
168 return FFI_DEFAULT_ABI;
169 }
170
171 static void psi_ffi_handler(ffi_cif *sig, void *result, void **args, void *data)
172 {
173 struct psi_impl *impl = data;
174 struct psi_ffi_impl_info *info = impl->info;
175
176 psi_context_call(info->context, *(zend_execute_data **)args[0], *(zval **)args[1], impl);
177 }
178
179 static void psi_ffi_callback(ffi_cif *sig, void *result, void **args, void *data)
180 {
181 struct psi_ffi_callback_info *cb_info = data;
182 struct psi_call_frame_callback cb_data;
183
184 assert(cb_info->impl_info->frame);
185
186 cb_data.cb = cb_info->let_exp;
187 cb_data.argc = sig->nargs;
188 cb_data.argv = args;
189 cb_data.rval = result;
190
191 psi_call_frame_do_callback(cb_info->impl_info->frame, &cb_data);
192 }
193
194 static bool psi_ffi_load()
195 {
196 #if HAVE_INT128
197 ffi_type *i128, *u128;
198
199 i128 = pecalloc(1, 3*sizeof(ffi_type), 1);
200 i128->type = FFI_TYPE_STRUCT;
201 i128->size = 0;
202 i128->elements = (ffi_type **) (i128 + 1);
203 i128->elements[0] = &ffi_type_sint64;
204 i128->elements[1] = &ffi_type_sint64;
205
206 ffi_type_sint128 = i128;
207
208 u128 = pecalloc(1, 3*sizeof(ffi_type), 1);
209 u128->type = FFI_TYPE_STRUCT;
210 u128->size = 0;
211 u128->elements = (ffi_type **) (u128 + 1);
212 u128->elements[0] = &ffi_type_uint64;
213 u128->elements[1] = &ffi_type_uint64;
214
215 ffi_type_uint128 = u128;
216 #endif
217 return true;
218 }
219
220 static void psi_ffi_free()
221 {
222 #if HAVE_INT128
223 free(ffi_type_sint128);
224 free(ffi_type_uint128);
225 #endif
226 }
227
228 static bool psi_ffi_init(struct psi_context *C)
229 {
230 ffi_status rc;
231 struct psi_ffi_context *context = pecalloc(1, sizeof(*context), 1);
232 ffi_abi abi = FFI_DEFAULT_ABI;
233
234 #if HAVE_FFI_FASTCALL
235 abi = FFI_FASTCALL;
236 #endif
237
238 context->params[0] = &ffi_type_pointer;
239 context->params[1] = &ffi_type_pointer;
240 rc = ffi_prep_cif(&context->signature, abi, 2, &ffi_type_void,
241 context->params);
242
243 if (FFI_OK != rc) {
244 pefree(context, 1);
245 return false;
246 }
247
248 C->context = context;
249 return true;
250 }
251
252 static void psi_ffi_dtor(struct psi_context *C)
253 {
254 if (C->context) {
255 pefree(C->context, 1);
256 C->context = NULL;
257 }
258 }
259
260 static bool psi_ffi_composite_init(struct psi_context *C,
261 struct psi_decl_arg *darg)
262 {
263 struct psi_ffi_struct_info *info;
264
265 if (darg->engine.type) {
266 return true;
267 }
268
269 info = pecalloc(1, sizeof(*info), 1);
270 info->eles = psi_plist_init((psi_plist_dtor) psi_ffi_type_free);
271
272 psi_context_composite_type_elements(C, darg, &info->eles);
273
274 /* add terminating NULL; libffi structs do not have an element count */
275 {
276 void *null_ptr = NULL;
277 info->eles = psi_plist_add(info->eles, &null_ptr);
278 }
279
280 info->strct.type = FFI_TYPE_STRUCT;
281 info->strct.alignment = 0;
282 info->strct.size = 0;
283 info->strct.elements = (ffi_type **) psi_plist_eles(info->eles);
284
285 darg->engine.info = info;
286 darg->engine.type = &info->strct;
287
288 return true;
289 }
290
291 static void psi_ffi_composite_dtor(struct psi_context *C,
292 struct psi_decl_arg *darg)
293 {
294 struct psi_ffi_struct_info *info = darg->engine.info;
295
296 if (info) {
297 darg->engine.info = NULL;
298 darg->engine.type = NULL;
299
300 psi_plist_free(info->eles);
301 pefree(info, 1);
302 }
303 }
304
305 static void psi_ffi_extvar_get(ffi_cif *sig, void *result, void **args, void *data) {
306 struct psi_decl_extvar *evar = data;
307
308 psi_decl_extvar_get(evar, result);
309 }
310
311 static void psi_ffi_extvar_set(ffi_cif *sig, void *result, void **args, void *data) {
312 struct psi_decl_extvar *evar = data;
313
314 psi_decl_extvar_set(evar, args[0]);
315 }
316
317 static bool psi_ffi_decl_init(struct psi_context *, struct psi_decl *);
318
319 static bool psi_ffi_extvar_init(struct psi_context *C,
320 struct psi_decl_extvar *evar)
321 {
322 struct psi_ffi_extvar_info *info = pecalloc(1, sizeof(*info), 1);
323 ffi_status rc;
324
325 evar->info = info;
326
327 psi_ffi_decl_init(C, evar->getter);
328 psi_ffi_decl_init(C, evar->setter);
329
330 rc = ffi_prep_cif(&info->get.signature, FFI_DEFAULT_ABI, 0,
331 psi_context_decl_arg_call_type(C, evar->getter->func), NULL);
332 if (FFI_OK != rc) {
333 return false;
334 }
335 rc = psi_ffi_prep_closure(&info->get.closure, &info->get.code,
336 &info->get.signature, psi_ffi_extvar_get, evar);
337 if (FFI_OK != rc) {
338 return false;
339 }
340
341 info->set.params[0] = psi_context_decl_arg_call_type(C, evar->arg);
342 rc = ffi_prep_cif(&info->set.signature, FFI_DEFAULT_ABI, 1,
343 &ffi_type_void, info->set.params);
344 if (FFI_OK != rc) {
345 return false;
346 }
347 rc = psi_ffi_prep_closure(&info->set.closure, &info->set.code,
348 &info->set.signature, psi_ffi_extvar_set, evar);
349 if (FFI_OK != rc) {
350 return false;
351 }
352
353 evar->getter->sym = info->get.code;
354 evar->setter->sym = info->set.code;
355
356 return true;
357 }
358
359 static void psi_ffi_extvar_dtor(struct psi_context *C,
360 struct psi_decl_extvar *evar) {
361 if (evar->info) {
362 pefree(evar->info, 1);
363 evar->info = NULL;
364 }
365 }
366
367 static bool psi_ffi_decl_init(struct psi_context *C, struct psi_decl *decl)
368 {
369 if (!decl->info) {
370 int rc;
371 size_t i, c = psi_plist_count(decl->args);
372 struct psi_decl_arg *arg;
373 struct psi_ffi_decl_info *info = pecalloc(1,
374 sizeof(*info) + 2 * c * sizeof(void *), 1);
375
376 decl->info = info;
377
378 for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
379 info->params[i] = psi_context_decl_arg_call_type(C, arg);
380 }
381 info->params[c] = NULL;
382
383 rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
384 c, psi_context_decl_arg_call_type(C, decl->func), info->params);
385
386 if (FFI_OK != rc) {
387 pefree(info, 1);
388 decl->info = NULL;
389 return false;
390 }
391 }
392
393 return true;
394 }
395
396 static void psi_ffi_decl_dtor(struct psi_context *C,
397 struct psi_decl *decl)
398 {
399 if (decl->info) {
400 pefree(decl->info, 1);
401 decl->info = NULL;
402 }
403 }
404
405 static bool psi_ffi_impl_init(struct psi_context *C,
406 struct psi_impl *impl, zif_handler *zh)
407 {
408 struct psi_ffi_context *context = C->context;
409 struct psi_ffi_impl_info *info = pecalloc(1, sizeof(*info), 1);
410 ffi_status rc;
411
412 impl->info = info;
413 info->context = C;
414
415 rc = psi_ffi_prep_closure(&info->closure, &info->code,
416 &context->signature, psi_ffi_handler, impl);
417
418 if (FFI_OK != rc) {
419 goto failure;
420 }
421
422 *zh = info->code;
423 return true;
424
425 failure: ;
426 impl->info = NULL;
427 free(info);
428 return false;
429 }
430
431 static void psi_ffi_impl_dtor(struct psi_context *C, struct psi_impl *impl)
432 {
433 struct psi_ffi_impl_info *info = impl->info;
434
435 if (info) {
436 if (info->closure) {
437 psi_ffi_closure_free(info->closure);
438 }
439 free(info);
440 impl->info = NULL;
441 }
442 }
443
444 static bool psi_ffi_cb_init(struct psi_context *C,
445 struct psi_let_exp *exp, struct psi_impl *impl)
446 {
447 struct psi_ffi_callback_info *cb_info;
448 struct psi_ffi_decl_info *decl_info;
449 ffi_status rc;
450
451 assert(exp->kind == PSI_LET_CALLBACK);
452
453 if (!psi_ffi_decl_init(C, exp->data.callback->decl)) {
454 return false;
455 }
456
457 cb_info = pecalloc(1, sizeof(*cb_info), 1);
458 cb_info->impl_info = impl->info;
459 cb_info->let_exp = exp;
460
461 decl_info = exp->data.callback->decl->info;
462 rc = psi_ffi_prep_closure(&cb_info->closure, &cb_info->code,
463 &decl_info->signature, psi_ffi_callback, cb_info);
464
465 if (FFI_OK != rc) {
466 free(cb_info);
467 return false;
468 }
469
470 assert(!exp->data.callback->decl->sym);
471 exp->data.callback->info = cb_info;
472 exp->data.callback->decl->sym = cb_info->code;
473
474 return true;
475 }
476
477 static void psi_ffi_cb_dtor(struct psi_context *C,
478 struct psi_let_exp *let_exp, struct psi_impl *impl)
479 {
480 assert(let_exp->kind == PSI_LET_CALLBACK);
481
482 psi_ffi_decl_dtor(C, let_exp->data.callback->decl);
483
484 if (let_exp->data.callback->info) {
485 struct psi_ffi_callback_info *info = let_exp->data.callback->info;
486
487 if (info->closure) {
488 psi_ffi_closure_free(info->closure);
489 }
490 pefree(info, 1);
491 let_exp->data.callback->info = NULL;
492 }
493 }
494
495 static void psi_ffi_call(struct psi_call_frame *frame) {
496 struct psi_decl *decl = psi_call_frame_get_decl(frame);
497 struct psi_impl *impl = psi_call_frame_get_impl(frame);
498 struct psi_ffi_decl_info *decl_info = decl->info;
499 struct psi_ffi_impl_info *impl_info;
500 struct psi_call_frame *prev;
501
502 if (impl) {
503 impl_info = impl->info;
504 prev = impl_info->frame;
505 impl_info->frame = frame;
506 }
507 ffi_call(&decl_info->signature, FFI_FN(decl->sym),
508 psi_call_frame_get_rpointer(frame),
509 psi_call_frame_get_arg_pointers(frame));
510 if (impl) {
511 impl_info->frame = prev;
512 }
513 }
514
515 static void psi_ffi_call_va(struct psi_call_frame *frame) {
516 ffi_cif signature;
517 struct psi_call_frame *prev;
518 struct psi_decl *decl = psi_call_frame_get_decl(frame);
519 struct psi_impl *impl = psi_call_frame_get_impl(frame);
520 struct psi_ffi_decl_info *decl_info = decl->info;
521 struct psi_ffi_impl_info *impl_info;
522 size_t i, va_count, argc;
523 ffi_type **param_types;
524
525 argc = psi_plist_count(decl->args);
526 va_count = psi_call_frame_num_var_args(frame);
527 param_types = ecalloc(argc + va_count + 1, sizeof(ffi_type *));
528 memcpy(param_types, decl_info->params, argc * sizeof(ffi_type *));
529 for (i = 0; i < va_count; ++i) {
530 struct psi_call_frame_argument *frame_arg;
531
532 frame_arg = psi_call_frame_get_var_argument(frame, i);
533 param_types[argc + i] = psi_ffi_impl_type(frame_arg->va_type);
534 }
535
536 psi_ffi_prep_va(&decl_info->signature, &signature, argc, va_count, param_types);
537
538 if (impl) {
539 impl_info = impl->info;
540 prev = impl_info->frame;
541 impl_info->frame = frame;
542 }
543 ffi_call(&signature, FFI_FN(decl->sym),
544 psi_call_frame_get_rpointer(frame),
545 psi_call_frame_get_arg_pointers(frame));
546 if (impl) {
547 impl_info->frame = prev;
548 }
549
550 efree(param_types);
551 }
552
553 static void *psi_ffi_typeof_impl(struct psi_context *C, token_t impl_type)
554 {
555 return psi_ffi_impl_type(impl_type);
556 }
557
558 static void *psi_ffi_typeof_decl(struct psi_context *C, token_t decl_type)
559 {
560 return psi_ffi_token_type(decl_type);
561 }
562
563 static void *psi_ffi_copyof_type(struct psi_context *C, void *orig_type)
564 {
565 ffi_type *type = pemalloc(sizeof(*type), 1);
566
567 *type = *(ffi_type *) orig_type;
568 return type;
569 }
570
571 static void psi_ffi_layoutof_type(struct psi_context *C, void *orig_type,
572 struct psi_layout *l)
573 {
574 ffi_type *type = orig_type;
575
576 if (!type->size || !type->alignment) {
577 ffi_cif tmp;
578 ffi_prep_cif(&tmp, FFI_DEFAULT_ABI, 0, type, NULL);
579 }
580
581 l->pos = type->alignment;
582 l->len = type->size;
583 }
584
585 static struct psi_context_ops ops = {
586 "libffi",
587 psi_ffi_load,
588 psi_ffi_free,
589 psi_ffi_init,
590 psi_ffi_dtor,
591 psi_ffi_composite_init,
592 psi_ffi_composite_dtor,
593 psi_ffi_extvar_init,
594 psi_ffi_extvar_dtor,
595 psi_ffi_decl_init,
596 psi_ffi_decl_dtor,
597 psi_ffi_impl_init,
598 psi_ffi_impl_dtor,
599 psi_ffi_cb_init,
600 psi_ffi_cb_dtor,
601 psi_ffi_call,
602 psi_ffi_call_va,
603 psi_ffi_typeof_impl,
604 psi_ffi_typeof_decl,
605 psi_ffi_copyof_type,
606 psi_ffi_layoutof_type,
607 };
608
609 struct psi_context_ops *psi_libffi_ops(void)
610 {
611 return &ops;
612 }
613
614 #endif /* HAVE_LIBFFI */