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