1569693ee559e883e0c573de46f39124dc768281
[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 #undef PACKAGE
34 #undef PACKAGE_BUGREPORT
35 #undef PACKAGE_NAME
36 #undef PACKAGE_STRING
37 #undef PACKAGE_TARNAME
38 #undef PACKAGE_VERSION
39
40 #include <ffi.h>
41
42 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
43 # if HAVE_UNISTD_H
44 # include <unistd.h>
45 # endif
46 # if HAVE_SYS_MMAN_H
47 # include <sys/mman.h>
48 # ifndef MAP_ANONYMOUS
49 # define MAP_ANONYMOUS MAP_ANON
50 # endif
51 # endif
52 #endif
53
54 static void *psi_ffi_closure_alloc(size_t s, void **code)
55 {
56 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
57 return ffi_closure_alloc(s, code);
58 #elif HAVE_MMAP
59 *code = mmap(NULL, s, PROT_EXEC|PROT_WRITE|PROT_READ,
60 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
61 if (MAP_FAILED == *code) {
62 return NULL;
63 }
64 return *code;
65 #else
66 # error "Neither ffi_closure_alloc() nor mmap() available"
67 #endif
68 }
69
70 static ffi_status psi_ffi_prep_closure(ffi_closure **closure, void **code, ffi_cif *sig, void (*handler)(ffi_cif*,void*,void**,void*), void *data) {
71 *closure = psi_ffi_closure_alloc(sizeof(ffi_closure), code);
72 assert(*closure != NULL);
73
74 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
75 return ffi_prep_closure_loc(*closure, sig, handler, data, *code);
76
77 #elif PSI_HAVE_FFI_PREP_CLOSURE
78 return ffi_prep_closure(*code, sig, handler, data);
79 #else
80 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
81 #endif
82 }
83
84 static void psi_ffi_closure_free(void *c)
85 {
86 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
87 ffi_closure_free(c);
88 #elif HAVE_MMAP
89 munmap(c, sizeof(ffi_closure));
90 #endif
91 }
92
93 static void psi_ffi_prep_va(ffi_cif *base, ffi_cif *signature, size_t argc, size_t va_count,
94 ffi_type **param_types) {
95 ffi_status rc;
96
97 #ifdef PSI_HAVE_FFI_PREP_CIF_VAR
98 rc = ffi_prep_cif_var(signature, base->abi, argc, argc + va_count,
99 base->rtype, param_types);
100 #else
101 /* FIXME: test in config.m4; assume we can just call anyway */
102 rc = ffi_prep_cif(signature, base->abi, argc + va_count, base->rtype, param_types);
103 #endif
104
105 assert(FFI_OK == rc);
106 }
107
108 static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg);
109
110 static inline ffi_type *psi_ffi_token_type(token_t t) {
111 switch (t) {
112 default:
113 assert(0);
114 /* no break */
115 case PSI_T_VOID:
116 return &ffi_type_void;
117 case PSI_T_INT8:
118 return &ffi_type_sint8;
119 case PSI_T_UINT8:
120 return &ffi_type_uint8;
121 case PSI_T_INT16:
122 return &ffi_type_sint16;
123 case PSI_T_UINT16:
124 return &ffi_type_uint16;
125 case PSI_T_INT32:
126 return &ffi_type_sint32;
127 case PSI_T_UINT32:
128 return &ffi_type_uint32;
129 case PSI_T_INT64:
130 return &ffi_type_sint64;
131 case PSI_T_UINT64:
132 return &ffi_type_uint64;
133 case PSI_T_BOOL:
134 return &ffi_type_uchar;
135 case PSI_T_ENUM:
136 return &ffi_type_sint;
137 case PSI_T_FLOAT:
138 return &ffi_type_float;
139 case PSI_T_DOUBLE:
140 return &ffi_type_double;
141 #ifdef HAVE_LONG_DOUBLE
142 case PSI_T_LONG_DOUBLE:
143 return &ffi_type_longdouble;
144 #endif
145 case PSI_T_POINTER:
146 case PSI_T_FUNCTION:
147 return &ffi_type_pointer;
148 }
149 }
150 static inline ffi_type *psi_ffi_impl_type(token_t impl_type) {
151 switch (impl_type) {
152 case PSI_T_BOOL:
153 return &ffi_type_sint8;
154 case PSI_T_INT:
155 return &ffi_type_sint64;
156 case PSI_T_STRING:
157 return &ffi_type_pointer;
158 case PSI_T_FLOAT:
159 case PSI_T_DOUBLE:
160 return &ffi_type_double;
161 EMPTY_SWITCH_DEFAULT_CASE();
162 }
163 return NULL;
164 }
165 static void psi_ffi_struct_type_dtor(void *type) {
166 ffi_type *strct = type;
167
168 if (strct->elements) {
169 ffi_type **ptr;
170
171 for (ptr = strct->elements; *ptr; ++ptr) {
172 free(*ptr);
173 }
174 free(strct->elements);
175 }
176 free(strct);
177 }
178
179 static size_t psi_ffi_struct_type_pad(ffi_type **els, size_t padding) {
180 size_t i;
181
182 for (i = 0; i < padding; ++i) {
183 ffi_type *pad = malloc(sizeof(*pad));
184
185 memcpy(pad, &ffi_type_schar, sizeof(*pad));
186 *els++ = pad;
187 }
188
189 return padding;
190 }
191
192 static ffi_type **psi_ffi_struct_type_elements(struct psi_decl_struct *strct) {
193 size_t i = 0, argc, nels = 0, offset = 0, maxalign = 0, last_arg_pos = -1;
194 ffi_type **tmp, **els;
195 struct psi_decl_arg *darg;
196
197 argc = psi_plist_count(strct->args);
198 els = calloc(argc + 1, sizeof(*els));
199
200 while (psi_plist_get(strct->args, i++, &darg)) {
201 ffi_type *type;
202 size_t padding;
203
204 if (darg->layout->pos == last_arg_pos) {
205 /* skip bit fields */
206 continue;
207 }
208 last_arg_pos = darg->layout->pos;
209
210 type = malloc(sizeof(*type));
211 *type = *psi_ffi_decl_arg_type(darg);
212
213 if (type->alignment > maxalign) {
214 maxalign = type->alignment;
215 }
216
217 assert(type->size <= darg->layout->len);
218 if ((padding = psi_offset_padding(darg->layout->pos - offset, type->alignment))) {
219 if (nels + padding + 1 > argc) {
220 argc += padding;
221 tmp = realloc(els, (argc + 1) * sizeof(*els));
222 if (tmp) {
223 els = tmp;
224 } else {
225 free(els);
226 return NULL;
227 }
228 els[argc] = NULL;
229 }
230 psi_ffi_struct_type_pad(&els[nels], padding);
231 nels += padding;
232 offset += padding;
233 }
234 assert(offset == darg->layout->pos);
235
236 offset = (offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
237 els[nels++] = type;
238 }
239
240 /* apply struct alignment padding */
241 offset = (offset + maxalign - 1) & ~(maxalign - 1);
242
243 assert(offset <= strct->size);
244 if (offset < strct->size) {
245 size_t padding = strct->size - offset;
246
247 tmp = realloc(els, (padding + argc + 1) * sizeof(*els));
248 if (tmp) {
249 els = tmp;
250 } else {
251 free(els);
252 return NULL;
253 }
254 psi_ffi_struct_type_pad(&els[nels], padding);
255 els[argc + padding] = NULL;
256 }
257
258 return els;
259 }
260 static inline ffi_type *psi_ffi_decl_type(struct psi_decl_type *type) {
261 struct psi_decl_type *real = psi_decl_type_get_real(type);
262
263 switch (real->type) {
264 case PSI_T_STRUCT:
265 if (!real->real.strct->engine.type) {
266 ffi_type *strct = calloc(1, sizeof(ffi_type));
267
268 strct->type = FFI_TYPE_STRUCT;
269 strct->size = 0;
270 strct->elements = psi_ffi_struct_type_elements(real->real.strct);
271
272 real->real.strct->engine.type = strct;
273 real->real.strct->engine.dtor = psi_ffi_struct_type_dtor;
274 }
275
276 return real->real.strct->engine.type;
277
278 case PSI_T_UNION:
279 {
280 struct psi_decl_arg *arg;
281 psi_plist_get(real->real.unn->args, 0, &arg);
282 return psi_ffi_decl_arg_type(arg);
283 }
284
285 default:
286 return psi_ffi_token_type(real->type);
287 }
288 }
289 static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg) {
290 if (darg->var->pointer_level) {
291 return &ffi_type_pointer;
292 } else {
293 return psi_ffi_decl_type(darg->type);
294 }
295 }
296
297 static inline ffi_abi psi_ffi_abi(const char *convention) {
298 if (FFI_LAST_ABI - 2 != FFI_FIRST_ABI) {
299 #ifdef HAVE_FFI_STDCALL
300 if (!strcasecmp(convention, "stdcall")) {
301 return FFI_STDCALL;
302 }
303 #endif
304 #ifdef HAVE_FFI_FASTCALL
305 if (!strcasecmp(convention, "fastcall")) {
306 return FFI_FASTCALL;
307 }
308 #endif
309 }
310 return FFI_DEFAULT_ABI;
311 }
312
313 struct psi_ffi_context {
314 ffi_cif signature;
315 ffi_type *params[2];
316 };
317
318 struct psi_ffi_impl_info {
319 struct psi_context *context;
320 struct psi_call_frame *frame;
321
322 void *code;
323 ffi_closure *closure;
324 };
325
326 struct psi_ffi_callback_info {
327 struct psi_ffi_impl_info *impl_info;
328 struct psi_let_exp *let_exp;
329
330 void *code;
331 ffi_closure *closure;
332 };
333
334 struct psi_ffi_decl_info {
335 ffi_cif signature;
336 ffi_type *params[1];
337 };
338
339 static inline struct psi_ffi_decl_info *psi_ffi_decl_init(struct psi_decl *decl) {
340 if (!decl->info) {
341 int rc;
342 size_t i, c = psi_plist_count(decl->args);
343 struct psi_decl_arg *arg;
344 struct psi_ffi_decl_info *info = calloc(1, sizeof(*info) + 2 * c * sizeof(void *));
345
346 for (i = 0; psi_plist_get(decl->args, i, &arg); ++i) {
347 info->params[i] = psi_ffi_decl_arg_type(arg);
348 }
349 info->params[c] = NULL;
350
351 rc = ffi_prep_cif(&info->signature, psi_ffi_abi(decl->abi->convention),
352 c, psi_ffi_decl_arg_type(decl->func), info->params);
353
354 if (FFI_OK != rc) {
355 free(info);
356 } else {
357 decl->info = info;
358 }
359 }
360
361 return decl->info;
362 }
363
364 static inline void psi_ffi_decl_dtor(struct psi_decl *decl) {
365 if (decl->info) {
366 free(decl->info);
367 decl->info = NULL;
368 }
369 }
370
371 static void psi_ffi_handler(ffi_cif *sig, void *result, void **args, void *data)
372 {
373 struct psi_impl *impl = data;
374 struct psi_ffi_impl_info *info = impl->info;
375
376 psi_context_call(info->context, *(zend_execute_data **)args[0], *(zval **)args[1], impl);
377 }
378
379 static void psi_ffi_callback(ffi_cif *sig, void *result, void **args, void *data)
380 {
381 struct psi_ffi_callback_info *cb_info = data;
382 struct psi_call_frame_callback cb_data;
383
384 assert(cb_info->impl_info->frame);
385
386 cb_data.cb = cb_info->let_exp;
387 cb_data.argc = sig->nargs;
388 cb_data.argv = args;
389 cb_data.rval = result;
390
391 psi_call_frame_do_callback(cb_info->impl_info->frame, &cb_data);
392 }
393
394 static inline void psi_ffi_callback_init(struct psi_ffi_impl_info *impl_info,
395 struct psi_let_exp *let_exp) {
396 struct psi_ffi_callback_info *cb_info;
397 struct psi_ffi_decl_info *decl_info;
398 struct psi_let_callback *cb;
399 struct psi_let_func *fn = NULL;
400 ffi_status rc;
401
402 switch (let_exp->kind) {
403 case PSI_LET_CALLBACK:
404 cb = let_exp->data.callback;
405 if (cb->decl->info) {
406 decl_info = cb->decl->info;
407 } else {
408 decl_info = psi_ffi_decl_init(cb->decl);
409 }
410
411 cb_info = calloc(1, sizeof(*cb_info));
412 cb_info->impl_info = impl_info;
413 cb_info->let_exp = let_exp;
414 rc = psi_ffi_prep_closure(&cb_info->closure, &cb_info->code,
415 &decl_info->signature, psi_ffi_callback, cb_info);
416
417 if (FFI_OK != rc) {
418 free(cb_info);
419 break;
420 }
421 cb->info = cb_info;
422
423 assert(!cb->decl->sym);
424 cb->decl->sym = cb_info->code;
425 fn = cb->func;
426 /* no break */
427
428 case PSI_LET_FUNC:
429 if (!fn) {
430 fn = let_exp->data.func;
431 }
432 if (fn->inner) {
433 size_t i = 0;
434 struct psi_let_exp *inner_let;
435
436 while (psi_plist_get(fn->inner, i++, &inner_let)) {
437 psi_ffi_callback_init(impl_info, inner_let);
438 }
439 }
440 break;
441 default:
442 break;
443 }
444 }
445
446 static inline void psi_ffi_callback_dtor(struct psi_let_exp *let_exp) {
447 struct psi_let_callback *cb;
448 struct psi_let_func *fn = NULL;
449
450 switch (let_exp->kind) {
451 case PSI_LET_CALLBACK:
452 cb = let_exp->data.callback;
453
454 psi_ffi_decl_dtor(cb->decl);
455
456 if (cb->info) {
457 struct psi_ffi_callback_info *info = cb->info;
458
459 if (info->closure) {
460 psi_ffi_closure_free(info->closure);
461 }
462 free(info);
463 cb->info = NULL;
464 }
465 fn = cb->func;
466 /* no break */
467 case PSI_LET_FUNC:
468 if (!fn) {
469 fn = let_exp->data.func;
470 }
471
472 if (fn->inner) {
473 size_t i = 0;
474 struct psi_let_exp *cb;
475
476 while (psi_plist_get(fn->inner, i++, &cb)) {
477 psi_ffi_callback_dtor(cb);
478 }
479 }
480 break;
481 default:
482 break;
483 }
484 }
485
486 static inline struct psi_ffi_impl_info *psi_ffi_impl_init(struct psi_impl *impl,
487 struct psi_context *C) {
488 struct psi_ffi_context *context = C->context;
489 struct psi_ffi_impl_info *info = calloc(1, sizeof(*info));
490 struct psi_let_stmt *let;
491 ffi_status rc;
492 size_t l = 0;
493
494 info->context = C;
495
496 rc = psi_ffi_prep_closure(&info->closure, &info->code,
497 &context->signature, psi_ffi_handler, impl);
498
499 if (FFI_OK != rc) {
500 free(info);
501 return NULL;
502 }
503
504 while (psi_plist_get(impl->stmts.let, l++, &let)) {
505 psi_ffi_callback_init(info, let->exp);
506 }
507
508 return impl->info = info;
509 }
510
511 static inline void psi_ffi_impl_dtor(struct psi_impl *impl) {
512 struct psi_ffi_impl_info *info = impl->info;
513 struct psi_let_stmt *let;
514 size_t j = 0;
515
516 while (psi_plist_get(impl->stmts.let, j++, &let)) {
517 psi_ffi_callback_dtor(let->exp);
518 }
519
520 if (info) {
521 if (info->closure) {
522 psi_ffi_closure_free(info->closure);
523 }
524 free(info);
525 impl->info = NULL;
526 }
527 }
528
529
530 static inline struct psi_ffi_context *psi_ffi_context_init(struct psi_ffi_context *L) {
531 ffi_status rc;
532
533 if (!L) {
534 L = malloc(sizeof(*L));
535 }
536 memset(L, 0, sizeof(*L));
537
538 L->params[0] = &ffi_type_pointer;
539 L->params[1] = &ffi_type_pointer;
540 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
541 assert(rc == FFI_OK);
542
543 return L;
544 }
545
546 static inline void psi_ffi_context_free(struct psi_ffi_context **L) {
547 if (*L) {
548 free(*L);
549 *L = NULL;
550 }
551 }
552
553 static void psi_ffi_init(struct psi_context *C)
554 {
555 C->context = psi_ffi_context_init(NULL);
556 }
557
558 static void psi_ffi_dtor(struct psi_context *C)
559 {
560 if (C->decls) {
561 size_t i = 0;
562 struct psi_decl *decl;
563
564 while (psi_plist_get(C->decls, i++, &decl)) {
565 psi_ffi_decl_dtor(decl);
566 }
567
568 }
569 if (C->impls) {
570 size_t i = 0;
571 struct psi_impl *impl;
572
573 while (psi_plist_get(C->impls, i++, &impl)) {
574 psi_ffi_impl_dtor(impl);
575 }
576 }
577 psi_ffi_context_free((void *) &C->context);
578 }
579
580
581 static zend_function_entry *psi_ffi_compile(struct psi_context *C)
582 {
583 size_t i = 0, d = 0, nf = 0;
584 struct psi_impl *impl;
585 struct psi_decl *decl;
586 zend_function_entry *zfe;
587
588 if (!C->impls) {
589 return NULL;
590 }
591
592 zfe = calloc(psi_plist_count(C->impls) + 1, sizeof(*zfe));
593
594 while (psi_plist_get(C->impls, i++, &impl)) {
595 zend_function_entry *zf = &zfe[nf];
596
597 if (!impl->decl) {
598 continue;
599 }
600 if (!psi_ffi_decl_init(impl->decl)) {
601 continue;
602 }
603 if (!psi_ffi_impl_init(impl, C)) {
604 continue;
605 }
606
607 zf->fname = impl->func->name + (impl->func->name[0] == '\\');
608 zf->handler = ((struct psi_ffi_impl_info *) impl->info)->code;
609 zf->num_args = psi_plist_count(impl->func->args);
610 zf->arg_info = psi_internal_arginfo(impl);
611 ++nf;
612 }
613
614 while (psi_plist_get(C->decls, d++, &decl)) {
615 if (decl->info) {
616 continue;
617 }
618
619 psi_ffi_decl_init(decl);
620 }
621
622 return zfe;
623 }
624
625 static inline void psi_ffi_call_ex(struct psi_call_frame *frame) {
626 struct psi_decl *decl = psi_call_frame_get_decl(frame);
627 struct psi_impl *impl = psi_call_frame_get_impl(frame);
628 struct psi_ffi_decl_info *decl_info = decl->info;
629 struct psi_ffi_impl_info *impl_info;
630 struct psi_call_frame *prev;
631
632 if (impl) {
633 impl_info = impl->info;
634 prev = impl_info->frame;
635 impl_info->frame = frame;
636 }
637 ffi_call(&decl_info->signature, FFI_FN(decl->sym),
638 psi_call_frame_get_rpointer(frame),
639 psi_call_frame_get_arg_pointers(frame));
640 if (impl) {
641 impl_info->frame = prev;
642 }
643 }
644
645 static inline void psi_ffi_call_va(struct psi_call_frame *frame) {
646 ffi_cif signature;
647 struct psi_call_frame *prev;
648 struct psi_decl *decl = psi_call_frame_get_decl(frame);
649 struct psi_impl *impl = psi_call_frame_get_impl(frame);
650 struct psi_ffi_decl_info *decl_info = decl->info;
651 struct psi_ffi_impl_info *impl_info;
652 size_t i, va_count, argc;
653 ffi_type **param_types;
654
655 argc = psi_plist_count(decl->args);
656 va_count = psi_call_frame_num_var_args(frame);
657 param_types = ecalloc(argc + va_count + 1, sizeof(ffi_type *));
658 memcpy(param_types, decl_info->params, argc * sizeof(ffi_type *));
659 for (i = 0; i < va_count; ++i) {
660 struct psi_call_frame_argument *frame_arg;
661
662 frame_arg = psi_call_frame_get_var_argument(frame, i);
663 param_types[argc + i] = psi_ffi_impl_type(frame_arg->va_type);
664 }
665
666 psi_ffi_prep_va(&decl_info->signature, &signature, argc, va_count, param_types);
667
668 if (impl) {
669 impl_info = impl->info;
670 prev = impl_info->frame;
671 impl_info->frame = frame;
672 }
673 ffi_call(&signature, FFI_FN(decl->sym),
674 psi_call_frame_get_rpointer(frame),
675 psi_call_frame_get_arg_pointers(frame));
676 if (impl) {
677 impl_info->frame = prev;
678 }
679
680 efree(param_types);
681 }
682
683 static void psi_ffi_call(struct psi_call_frame *frame) {
684 if (psi_call_frame_num_var_args(frame)) {
685 psi_ffi_call_va(frame);
686 } else {
687 psi_ffi_call_ex(frame);
688 }
689 }
690
691 static void *psi_ffi_query(struct psi_context *C, enum psi_context_query q, void *arg) {
692 switch (q) {
693 case PSI_CONTEXT_QUERY_SELF:
694 return "ffi";
695 case PSI_CONTEXT_QUERY_TYPE:
696 return psi_ffi_impl_type(*(token_t *) arg);
697 }
698 return NULL;
699 }
700
701 static struct psi_context_ops ops = {
702 psi_ffi_init,
703 psi_ffi_dtor,
704 psi_ffi_compile,
705 psi_ffi_call,
706 psi_ffi_query,
707 };
708
709 struct psi_context_ops *psi_libffi_ops(void)
710 {
711 return &ops;
712 }
713
714 #endif /* HAVE_LIBFFI */