raising the head after a three-weeks refactoring
[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 ZEND_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 ZEND_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 ZEND_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 ZEND_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 **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 els = realloc(els, (argc + 1) * sizeof(*els));
310 els[argc] = NULL;
311 }
312 psi_ffi_struct_type_pad(&els[nels], padding);
313 nels += padding;
314 offset += padding;
315 }
316 assert(offset == darg->layout->pos);
317
318 offset = (offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
319 els[nels++] = type;
320 }
321
322 /* apply struct alignment padding */
323 offset = (offset + maxalign - 1) & ~(maxalign - 1);
324
325 assert(offset <= strct->size);
326 if (offset < strct->size) {
327 psi_ffi_struct_type_pad(&els[nels], strct->size - offset);
328 }
329
330 return els;
331 }
332 static inline ffi_type *psi_ffi_decl_type(struct psi_decl_type *type) {
333 struct psi_decl_type *real = psi_decl_type_get_real(type);
334
335 switch (real->type) {
336 case PSI_T_STRUCT:
337 if (!real->real.strct->engine.type) {
338 ffi_type *strct = calloc(1, sizeof(ffi_type));
339
340 strct->type = FFI_TYPE_STRUCT;
341 strct->size = 0;
342 strct->elements = psi_ffi_struct_type_elements(real->real.strct);
343
344 real->real.strct->engine.type = strct;
345 real->real.strct->engine.dtor = psi_ffi_struct_type_dtor;
346 }
347
348 return real->real.strct->engine.type;
349
350 case PSI_T_UNION:
351 {
352 struct psi_decl_arg *arg;
353 psi_plist_get(real->real.unn->args, 0, &arg);
354 return psi_ffi_decl_arg_type(arg);
355 }
356
357 default:
358 return psi_ffi_token_type(real->type);
359 }
360 }
361 static inline ffi_type *psi_ffi_decl_arg_type(struct psi_decl_arg *darg) {
362 if (darg->var->pointer_level) {
363 return &ffi_type_pointer;
364 } else {
365 return psi_ffi_decl_type(darg->type);
366 }
367 }
368
369
370 static inline struct psi_ffi_context *psi_ffi_context_init(struct psi_ffi_context *L) {
371 ffi_status rc;
372
373 if (!L) {
374 L = malloc(sizeof(*L));
375 }
376 memset(L, 0, sizeof(*L));
377
378 L->params[0] = &ffi_type_pointer;
379 L->params[1] = &ffi_type_pointer;
380 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
381 ZEND_ASSERT(rc == FFI_OK);
382
383 return L;
384 }
385
386 static inline void psi_ffi_context_free(struct psi_ffi_context **L) {
387 if (*L) {
388 free(*L);
389 *L = NULL;
390 }
391 }
392
393 static void psi_ffi_init(struct psi_context *C)
394 {
395 C->context = psi_ffi_context_init(NULL);
396 }
397
398 static inline void psi_ffi_destroy_callbacks(struct psi_context *C, struct psi_let_exp *let_exp) {
399 struct psi_let_callback *cb;
400 struct psi_let_func *fn = NULL;
401
402 switch (let_exp->kind) {
403 case PSI_LET_CALLBACK:
404 cb = let_exp->data.callback;
405
406 if (cb->decl && cb->decl->info) {
407 psi_ffi_call_free(cb->decl->info);
408 }
409 fn = cb->func;
410 /* no break */
411 case PSI_LET_FUNC:
412 if (!fn) {
413 fn = let_exp->data.func;
414 }
415
416 if (fn->inner) {
417 size_t i = 0;
418 struct psi_let_exp *cb;
419
420 while (psi_plist_get(fn->inner, i++, &cb)) {
421 psi_ffi_destroy_callbacks(C, cb);
422 }
423 }
424 break;
425 default:
426 break;
427 }
428 }
429
430 static void psi_ffi_dtor(struct psi_context *C)
431 {
432 if (C->decls) {
433 size_t i = 0;
434 struct psi_decl *decl;
435
436 while (psi_plist_get(C->decls, i++, &decl)) {
437 if (decl->info) {
438 psi_ffi_call_free(decl->info);
439 }
440 }
441
442 }
443 if (C->impls) {
444 size_t i = 0;
445 struct psi_impl *impl;
446
447 while (psi_plist_get(C->impls, i++, &impl)) {
448 size_t j = 0;
449 struct psi_let_stmt *let;
450
451 while (psi_plist_get(impl->stmts.let, j++, &let)) {
452 psi_ffi_destroy_callbacks(C, let->exp);
453 }
454 }
455 }
456 psi_ffi_context_free((void *) &C->context);
457 }
458
459 static inline void psi_ffi_compile_callbacks(struct psi_context *C,
460 struct psi_ffi_call *impl_call, struct psi_let_exp *let_exp) {
461 struct psi_ffi_call *call;
462 struct psi_let_callback *cb;
463 struct psi_let_func *fn = NULL;
464
465 switch (let_exp->kind) {
466 case PSI_LET_CALLBACK:
467 cb = let_exp->data.callback;
468 if ((call = psi_ffi_call_alloc(C, cb->decl))) {
469 if (FFI_OK != psi_ffi_call_init_callback_closure(C, call, impl_call, let_exp)) {
470 psi_ffi_call_free(call);
471 break;
472 }
473
474 cb->decl->sym = call->code;
475 }
476 fn = cb->func;
477 /* no break */
478 case PSI_LET_FUNC:
479 if (!fn) {
480 fn = let_exp->data.func;
481 }
482 if (fn->inner) {
483 size_t i = 0;
484 struct psi_let_exp *inner_let;
485
486 while (psi_plist_get(fn->inner, i++, &inner_let)) {
487 psi_ffi_compile_callbacks(C, impl_call, inner_let);
488 }
489 }
490 break;
491 default:
492 break;
493 }
494 }
495
496 static zend_function_entry *psi_ffi_compile(struct psi_context *C)
497 {
498 size_t i = 0, d = 0, nf = 0;
499 struct psi_impl *impl;
500 struct psi_decl *decl;
501 zend_function_entry *zfe;
502
503 if (!C->impls) {
504 return NULL;
505 }
506
507 zfe = calloc(psi_plist_count(C->impls) + 1, sizeof(*zfe));
508
509 while (psi_plist_get(C->impls, i++, &impl)) {
510 size_t l = 0;
511 struct psi_let_stmt *let;
512 struct psi_ffi_call *call;
513 zend_function_entry *zf = &zfe[nf];
514
515 if (!impl->decl) {
516 continue;
517 }
518 if (!(call = psi_ffi_call_alloc(C, impl->decl))) {
519 continue;
520 }
521 if (FFI_OK != psi_ffi_call_init_closure(C, call, impl)) {
522 psi_ffi_call_free(call);
523 continue;
524 }
525
526 zf->fname = impl->func->name + (impl->func->name[0] == '\\');
527 zf->handler = call->code;
528 zf->num_args = psi_plist_count(impl->func->args);
529 zf->arg_info = psi_internal_arginfo(impl);
530 ++nf;
531
532 while (psi_plist_get(impl->stmts.let, l++, &let)) {
533 psi_ffi_compile_callbacks(C, call, let->exp);
534 }
535 }
536
537 while (psi_plist_get(C->decls, d++, &decl)) {
538 if (decl->info) {
539 continue;
540 }
541
542 psi_ffi_call_alloc(C, decl);
543 }
544
545 return zfe;
546 }
547
548 static void psi_ffi_call(struct psi_context *C, struct psi_call_frame *frame, struct psi_decl *decl, void *rval, void **args) {
549 struct psi_ffi_call *info = decl->info;
550 struct psi_call_frame *prev = info->impl.fn.frame;
551
552 info->impl.fn.frame = frame;
553 ffi_call(&info->signature, FFI_FN(decl->sym), rval, args);
554 info->impl.fn.frame = prev;
555 }
556
557 static void psi_ffi_call_va(struct psi_context *C, struct psi_call_frame *frame, struct psi_decl *decl, void *rval, void **args,
558 size_t va_count, void **va_types) {
559 ffi_cif signature;
560 struct psi_ffi_call *info = decl->info;
561 struct psi_call_frame *prev = info->impl.fn.frame;
562 size_t argc = psi_plist_count(decl->args);
563 ffi_type **param_types = ecalloc(argc + va_count + 1, sizeof(ffi_type *));
564
565 memcpy(param_types, info->params, argc * sizeof(ffi_type *));
566 memcpy(param_types + argc, va_types, va_count * sizeof(ffi_type *));
567
568 psi_ffi_prep_va(&info->signature, &signature, argc, va_count, param_types);
569 info->impl.fn.frame = frame;
570 ffi_call(&signature, FFI_FN(decl->sym), rval, args);
571 info->impl.fn.frame = prev;
572 efree(param_types);
573 }
574
575 static void *psi_ffi_query(struct psi_context *C, enum psi_context_query q, void *arg) {
576 switch (q) {
577 case PSI_CONTEXT_QUERY_SELF:
578 return "ffi";
579 case PSI_CONTEXT_QUERY_TYPE:
580 return psi_ffi_impl_type(*(token_t *) arg);
581 }
582 return NULL;
583 }
584
585 static struct psi_context_ops ops = {
586 psi_ffi_init,
587 psi_ffi_dtor,
588 psi_ffi_compile,
589 psi_ffi_call,
590 psi_ffi_call_va,
591 psi_ffi_query,
592 };
593
594 struct psi_context_ops *psi_libffi_ops(void)
595 {
596 return &ops;
597 }
598
599 #endif /* HAVE_LIBFFI */