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