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