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