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