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