6f808d4ce2360c4d1ba49079682e247a6fa72417
[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
233 context->params[0] = &ffi_type_pointer;
234 context->params[1] = &ffi_type_pointer;
235 rc = ffi_prep_cif(&context->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void,
236 context->params);
237
238 if (FFI_OK != rc) {
239 pefree(context, 1);
240 return false;
241 }
242
243 C->context = context;
244 return true;
245 }
246
247 static void psi_ffi_dtor(struct psi_context *C)
248 {
249 if (C->context) {
250 pefree(C->context, 1);
251 C->context = NULL;
252 }
253 }
254
255 static bool psi_ffi_composite_init(struct psi_context *C,
256 struct psi_decl_arg *darg)
257 {
258 struct psi_ffi_struct_info *info;
259
260 if (darg->engine.type) {
261 return true;
262 }
263
264 info = pecalloc(1, sizeof(*info), 1);
265 info->eles = psi_plist_init((psi_plist_dtor) psi_ffi_type_free);
266
267 psi_context_composite_type_elements(C, darg, &info->eles);
268
269 /* add terminating NULL; libffi structs do not have an element count */
270 {
271 void *null_ptr = NULL;
272 info->eles = psi_plist_add(info->eles, &null_ptr);
273 }
274
275 info->strct.type = FFI_TYPE_STRUCT;
276 info->strct.alignment = 0;
277 info->strct.size = 0;
278 info->strct.elements = (ffi_type **) psi_plist_eles(info->eles);
279
280 darg->engine.info = info;
281 darg->engine.type = &info->strct;
282
283 return true;
284 }
285
286 static void psi_ffi_composite_dtor(struct psi_context *C,
287 struct psi_decl_arg *darg)
288 {
289 struct psi_ffi_struct_info *info = darg->engine.info;
290
291 if (info) {
292 darg->engine.info = NULL;
293 darg->engine.type = NULL;
294
295 struct psi_plist *args = NULL;
296 struct psi_decl_type *dtype = psi_decl_type_get_real(darg->type);
297
298 if (dtype->type == PSI_T_STRUCT) {
299 args = dtype->real.strct->args;
300 } else if (dtype->type == PSI_T_UNION) {
301 args = dtype->real.unn->args;
302 }
303
304 size_t i = 0;
305 struct psi_decl_arg *tmp;
306
307 while (psi_plist_get(args, i++, &tmp)) {
308 psi_ffi_composite_dtor(C, tmp);
309 }
310
311 psi_plist_free(info->eles);
312 pefree(info, 1);
313 }
314 }
315
316 static void psi_ffi_extvar_get(ffi_cif *sig, void *result, void **args, void *data) {
317 struct psi_decl_extvar *evar = data;
318
319 psi_decl_extvar_get(evar, result);
320 }
321
322 static void psi_ffi_extvar_set(ffi_cif *sig, void *result, void **args, void *data) {
323 struct psi_decl_extvar *evar = data;
324
325 psi_decl_extvar_set(evar, args[0]);
326 }
327
328 static bool psi_ffi_decl_init(struct psi_context *, struct psi_decl *);
329
330 static bool psi_ffi_extvar_init(struct psi_context *C,
331 struct psi_decl_extvar *evar)
332 {
333 struct psi_ffi_extvar_info *info = pecalloc(1, sizeof(*info), 1);
334 ffi_status rc;
335
336 evar->info = info;
337
338 psi_ffi_decl_init(C, evar->getter);
339 psi_ffi_decl_init(C, evar->setter);
340
341 rc = ffi_prep_cif(&info->get.signature, FFI_DEFAULT_ABI, 0,
342 psi_context_decl_arg_call_type(C, evar->getter->func), NULL);
343 if (FFI_OK != rc) {
344 return false;
345 }
346 rc = psi_ffi_prep_closure(&info->get.closure, &info->get.code,
347 &info->get.signature, psi_ffi_extvar_get, evar);
348 if (FFI_OK != rc) {
349 return false;
350 }
351
352 info->set.params[0] = psi_context_decl_arg_call_type(C, evar->arg);
353 rc = ffi_prep_cif(&info->set.signature, FFI_DEFAULT_ABI, 1,
354 &ffi_type_void, info->set.params);
355 if (FFI_OK != rc) {
356 return false;
357 }
358 rc = psi_ffi_prep_closure(&info->set.closure, &info->set.code,
359 &info->set.signature, psi_ffi_extvar_set, evar);
360 if (FFI_OK != rc) {
361 return false;
362 }
363
364 evar->getter->sym = info->get.code;
365 evar->setter->sym = info->set.code;
366
367 return true;
368 }
369
370 static void psi_ffi_extvar_dtor(struct psi_context *C,
371 struct psi_decl_extvar *evar) {
372 if (evar->info) {
373 pefree(evar->info, 1);
374 evar->info = NULL;
375 }
376 }
377
378 static bool psi_ffi_decl_init(struct psi_context *C, struct psi_decl *decl)
379 {
380 if (!decl->info) {
381 int rc;
382 size_t i, c = psi_plist_count(decl->args);
383 struct psi_decl_arg *arg;
384 struct psi_ffi_decl_info *info = pecalloc(1,
385 sizeof(*info) + 2 * c * sizeof(void *), 1);
386
387 decl->info = info;
388
389 for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
390 info->params[i] = psi_context_decl_arg_call_type(C, arg);
391 }
392 info->params[c] = NULL;
393
394 rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
395 c, psi_context_decl_arg_call_type(C, decl->func), info->params);
396
397 if (FFI_OK != rc) {
398 pefree(info, 1);
399 decl->info = NULL;
400 return false;
401 }
402 }
403
404 return true;
405 }
406
407 static void psi_ffi_decl_dtor(struct psi_context *C,
408 struct psi_decl *decl)
409 {
410 if (decl->info) {
411 pefree(decl->info, 1);
412 decl->info = NULL;
413 }
414 }
415
416 static bool psi_ffi_impl_init(struct psi_context *C,
417 struct psi_impl *impl, zif_handler *zh)
418 {
419 struct psi_ffi_context *context = C->context;
420 struct psi_ffi_impl_info *info = pecalloc(1, sizeof(*info), 1);
421 ffi_status rc;
422
423 impl->info = info;
424 info->context = C;
425
426 rc = psi_ffi_prep_closure(&info->closure, &info->code,
427 &context->signature, psi_ffi_handler, impl);
428
429 if (FFI_OK != rc) {
430 goto failure;
431 }
432
433 *zh = info->code;
434 return true;
435
436 failure: ;
437 impl->info = NULL;
438 free(info);
439 return false;
440 }
441
442 static void psi_ffi_impl_dtor(struct psi_context *C, struct psi_impl *impl)
443 {
444 struct psi_ffi_impl_info *info = impl->info;
445
446 if (info) {
447 if (info->closure) {
448 psi_ffi_closure_free(info->closure);
449 }
450 free(info);
451 impl->info = NULL;
452 }
453 }
454
455 static bool psi_ffi_cb_init(struct psi_context *C,
456 struct psi_let_exp *exp, struct psi_impl *impl)
457 {
458 struct psi_ffi_callback_info *cb_info;
459 struct psi_ffi_decl_info *decl_info;
460 ffi_status rc;
461
462 assert(exp->kind == PSI_LET_CALLBACK);
463
464 if (!psi_ffi_decl_init(C, exp->data.callback->decl)) {
465 return false;
466 }
467
468 cb_info = pecalloc(1, sizeof(*cb_info), 1);
469 cb_info->impl_info = impl->info;
470 cb_info->let_exp = exp;
471
472 decl_info = exp->data.callback->decl->info;
473 rc = psi_ffi_prep_closure(&cb_info->closure, &cb_info->code,
474 &decl_info->signature, psi_ffi_callback, cb_info);
475
476 if (FFI_OK != rc) {
477 free(cb_info);
478 return false;
479 }
480
481 assert(!exp->data.callback->decl->sym);
482 exp->data.callback->info = cb_info;
483 exp->data.callback->decl->sym = cb_info->code;
484
485 return true;
486 }
487
488 static void psi_ffi_cb_dtor(struct psi_context *C,
489 struct psi_let_exp *let_exp, struct psi_impl *impl)
490 {
491 assert(let_exp->kind == PSI_LET_CALLBACK);
492
493 psi_ffi_decl_dtor(C, let_exp->data.callback->decl);
494
495 if (let_exp->data.callback->info) {
496 struct psi_ffi_callback_info *info = let_exp->data.callback->info;
497
498 if (info->closure) {
499 psi_ffi_closure_free(info->closure);
500 }
501 pefree(info, 1);
502 let_exp->data.callback->info = NULL;
503 }
504 }
505
506 static void psi_ffi_call(struct psi_call_frame *frame) {
507 struct psi_decl *decl = psi_call_frame_get_decl(frame);
508 struct psi_impl *impl = psi_call_frame_get_impl(frame);
509 struct psi_ffi_decl_info *decl_info = decl->info;
510 struct psi_ffi_impl_info *impl_info;
511 struct psi_call_frame *prev;
512
513 if (impl) {
514 impl_info = impl->info;
515 prev = impl_info->frame;
516 impl_info->frame = frame;
517 }
518 ffi_call(&decl_info->signature, FFI_FN(decl->sym),
519 psi_call_frame_get_rpointer(frame),
520 psi_call_frame_get_arg_pointers(frame));
521 if (impl) {
522 impl_info->frame = prev;
523 }
524 }
525
526 static void psi_ffi_call_va(struct psi_call_frame *frame) {
527 ffi_cif signature;
528 struct psi_call_frame *prev;
529 struct psi_decl *decl = psi_call_frame_get_decl(frame);
530 struct psi_impl *impl = psi_call_frame_get_impl(frame);
531 struct psi_ffi_decl_info *decl_info = decl->info;
532 struct psi_ffi_impl_info *impl_info;
533 size_t i, va_count, argc;
534 ffi_type **param_types;
535
536 argc = psi_plist_count(decl->args);
537 va_count = psi_call_frame_num_var_args(frame);
538 param_types = ecalloc(argc + va_count + 1, sizeof(ffi_type *));
539 memcpy(param_types, decl_info->params, argc * sizeof(ffi_type *));
540 for (i = 0; i < va_count; ++i) {
541 struct psi_call_frame_argument *frame_arg;
542
543 frame_arg = psi_call_frame_get_var_argument(frame, i);
544 param_types[argc + i] = psi_ffi_impl_type(frame_arg->va_type);
545 }
546
547 psi_ffi_prep_va(&decl_info->signature, &signature, argc, va_count, param_types);
548
549 if (impl) {
550 impl_info = impl->info;
551 prev = impl_info->frame;
552 impl_info->frame = frame;
553 }
554 ffi_call(&signature, FFI_FN(decl->sym),
555 psi_call_frame_get_rpointer(frame),
556 psi_call_frame_get_arg_pointers(frame));
557 if (impl) {
558 impl_info->frame = prev;
559 }
560
561 efree(param_types);
562 }
563
564 static void *psi_ffi_typeof_impl(struct psi_context *C, token_t impl_type)
565 {
566 return psi_ffi_impl_type(impl_type);
567 }
568
569 static void *psi_ffi_typeof_decl(struct psi_context *C, token_t decl_type)
570 {
571 return psi_ffi_token_type(decl_type);
572 }
573
574 static void *psi_ffi_copyof_type(struct psi_context *C, void *orig_type)
575 {
576 ffi_type *type = pemalloc(sizeof(*type), 1);
577
578 *type = *(ffi_type *) orig_type;
579 return type;
580 }
581
582 static void psi_ffi_layoutof_type(struct psi_context *C, void *orig_type,
583 struct psi_layout *l)
584 {
585 ffi_type *type = orig_type;
586
587 if (!type->size || !type->alignment) {
588 ffi_cif tmp;
589 ffi_prep_cif(&tmp, FFI_DEFAULT_ABI, 0, type, NULL);
590 }
591
592 l->pos = type->alignment;
593 l->len = type->size;
594 }
595
596 static struct psi_context_ops ops = {
597 "libffi",
598 psi_ffi_load,
599 psi_ffi_free,
600 psi_ffi_init,
601 psi_ffi_dtor,
602 psi_ffi_composite_init,
603 psi_ffi_composite_dtor,
604 psi_ffi_extvar_init,
605 psi_ffi_extvar_dtor,
606 psi_ffi_decl_init,
607 psi_ffi_decl_dtor,
608 psi_ffi_impl_init,
609 psi_ffi_impl_dtor,
610 psi_ffi_cb_init,
611 psi_ffi_cb_dtor,
612 psi_ffi_call,
613 psi_ffi_call_va,
614 psi_ffi_typeof_impl,
615 psi_ffi_typeof_decl,
616 psi_ffi_copyof_type,
617 psi_ffi_layoutof_type,
618 };
619
620 struct psi_context_ops *psi_libffi_ops(void)
621 {
622 return &ops;
623 }
624
625 #endif /* HAVE_LIBFFI */