validation and marshaling of structs/unions
[m6w6/ext-psi] / src / libffi.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include "php.h"
6
7 #ifdef HAVE_LIBFFI
8
9 #include "php_psi.h"
10 #include "engine.h"
11
12 #undef PACKAGE
13 #undef PACKAGE_BUGREPORT
14 #undef PACKAGE_NAME
15 #undef PACKAGE_STRING
16 #undef PACKAGE_TARNAME
17 #undef PACKAGE_VERSION
18
19 #include <ffi.h>
20
21 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
22 # if HAVE_UNISTD_H
23 # include <unistd.h>
24 # endif
25 # if HAVE_SYS_MMAN_H
26 # include <sys/mman.h>
27 # ifndef MAP_ANONYMOUS
28 # define MAP_ANONYMOUS MAP_ANON
29 # endif
30 # endif
31 #endif
32
33 static void *psi_ffi_closure_alloc(size_t s, void **code)
34 {
35 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
36 return ffi_closure_alloc(s, code);
37 #elif HAVE_MMAP
38 *code = mmap(NULL, s, PROT_EXEC|PROT_WRITE|PROT_READ,
39 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
40 if (MAP_FAILED == *code) {
41 return NULL;
42 }
43 return *code;
44 #else
45 # error "Neither ffi_closure_alloc() nor mmap() available"
46 #endif
47 }
48
49 static ffi_status psi_ffi_prep_closure(ffi_closure **closure, void **code, ffi_cif *sig, void (*handler)(ffi_cif*,void*,void**,void*), void *data) {
50 *closure = psi_ffi_closure_alloc(sizeof(ffi_closure), code);
51 ZEND_ASSERT(*closure != NULL);
52
53 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
54 return ffi_prep_closure_loc(*closure, sig, handler, data, *code);
55
56 #elif PSI_HAVE_FFI_PREP_CLOSURE
57 return ffi_prep_closure(*code, sig, handler, data);
58 #else
59 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
60 #endif
61
62 }
63
64 static void psi_ffi_closure_free(void *c)
65 {
66 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
67 ffi_closure_free(c);
68 #elif HAVE_MMAP
69 munmap(c, sizeof(ffi_closure));
70 #endif
71 }
72
73 static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
74 {
75 psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
76 }
77
78 static void psi_ffi_callback(ffi_cif *_sig, void *_result, void **_args, void *_data)
79 {
80 psi_callback(_data, _result, _sig->nargs, _args);
81 }
82
83 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg);
84
85 struct psi_ffi_context {
86 ffi_cif signature;
87 ffi_type *params[2];
88 };
89
90 struct psi_ffi_call {
91 void *code;
92 ffi_closure *closure;
93 ffi_cif signature;
94 void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
95 };
96
97 static inline ffi_abi psi_ffi_abi(const char *convention) {
98 return FFI_DEFAULT_ABI;
99 }
100
101 static inline struct psi_ffi_call *psi_ffi_call_alloc(struct psi_context *C, decl *decl) {
102 int rc;
103 size_t i, c = decl->args ? decl->args->count : 0;
104 struct psi_ffi_call *call = calloc(1, sizeof(*call) + 2 * c * sizeof(void *));
105
106 for (i = 0; i < c; ++i) {
107 call->params[i] = psi_ffi_decl_arg_type(decl->args->args[i]);
108 }
109 call->params[c] = NULL;
110
111 decl->call.info = call;
112 decl->call.rval = &decl->func->ptr;
113 decl->call.argc = c;
114 decl->call.args = (void **) &call->params[c+1];
115
116 rc = ffi_prep_cif(&call->signature, psi_ffi_abi(decl->abi->convention),
117 c, psi_ffi_decl_arg_type(decl->func), (ffi_type **) call->params);
118 ZEND_ASSERT(FFI_OK == rc);
119
120 return call;
121 }
122
123 static inline ffi_status psi_ffi_call_init_closure(struct psi_context *C, struct psi_ffi_call *call, impl *impl) {
124 struct psi_ffi_context *context = C->context;
125
126 return psi_ffi_prep_closure(&call->closure, &call->code, &context->signature, psi_ffi_handler, impl);
127 }
128
129 static inline ffi_status psi_ffi_call_init_callback_closure(struct psi_context *C, struct psi_ffi_call *call, let_callback *cb) {
130 return psi_ffi_prep_closure(&call->closure, &call->code, &call->signature, psi_ffi_callback, cb);
131 }
132
133 static inline void psi_ffi_call_free(struct psi_ffi_call *call) {
134 if (call->closure) {
135 psi_ffi_closure_free(call->closure);
136 }
137 free(call);
138 }
139
140 static inline ffi_type *psi_ffi_token_type(token_t t) {
141 switch (t) {
142 default:
143 ZEND_ASSERT(0);
144 /* no break */
145 case PSI_T_VOID:
146 return &ffi_type_void;
147 case PSI_T_INT8:
148 return &ffi_type_sint8;
149 case PSI_T_UINT8:
150 return &ffi_type_uint8;
151 case PSI_T_INT16:
152 return &ffi_type_sint16;
153 case PSI_T_UINT16:
154 return &ffi_type_uint16;
155 case PSI_T_INT32:
156 return &ffi_type_sint32;
157 case PSI_T_UINT32:
158 return &ffi_type_uint32;
159 case PSI_T_INT64:
160 return &ffi_type_sint64;
161 case PSI_T_UINT64:
162 return &ffi_type_uint64;
163 case PSI_T_BOOL:
164 return &ffi_type_uchar;
165 case PSI_T_INT:
166 case PSI_T_ENUM:
167 return &ffi_type_sint;
168 case PSI_T_LONG:
169 return &ffi_type_slong;
170 case PSI_T_FLOAT:
171 return &ffi_type_float;
172 case PSI_T_DOUBLE:
173 return &ffi_type_double;
174 #ifdef HAVE_LONG_DOUBLE
175 case PSI_T_LONG_DOUBLE:
176 return &ffi_type_longdouble;
177 #endif
178 case PSI_T_POINTER:
179 case PSI_T_FUNCTION:
180 return &ffi_type_pointer;
181 }
182 }
183 static inline ffi_type *psi_ffi_impl_type(token_t impl_type) {
184 switch (impl_type) {
185 case PSI_T_BOOL:
186 return &ffi_type_sint8;
187 case PSI_T_INT:
188 return &ffi_type_sint64;
189 case PSI_T_STRING:
190 return &ffi_type_pointer;
191 case PSI_T_FLOAT:
192 case PSI_T_DOUBLE:
193 return &ffi_type_double;
194 EMPTY_SWITCH_DEFAULT_CASE();
195 }
196 return NULL;
197 }
198 static void psi_ffi_struct_type_dtor(void *type) {
199 ffi_type *strct = type;
200
201 if (strct->elements) {
202 ffi_type **ptr;
203
204 for (ptr = strct->elements; *ptr; ++ptr) {
205 free(*ptr);
206 }
207 free(strct->elements);
208 }
209 free(strct);
210 }
211
212 static size_t psi_ffi_struct_type_pad(ffi_type **els, size_t padding) {
213 size_t i;
214
215 for (i = 0; i < padding; ++i) {
216 ffi_type *pad = malloc(sizeof(*pad));
217
218 memcpy(pad, &ffi_type_schar, sizeof(*pad));
219 *els++ = pad;
220 }
221
222 return padding;
223 }
224
225 static ffi_type **psi_ffi_struct_type_elements(decl_struct *strct) {
226 size_t i, argc = strct->args->count, nels = 0, offset = 0, maxalign = 0;
227 ffi_type **els = calloc(argc + 1, sizeof(*els));
228
229 for (i = 0; i < strct->args->count; ++i) {
230 decl_arg *darg = strct->args->args[i];
231 ffi_type *type = malloc(sizeof(*type));
232 size_t padding;
233
234 memcpy(type, psi_ffi_decl_arg_type(darg), sizeof(*type));
235
236 ZEND_ASSERT(type->size == darg->layout->len);
237
238 if (type->alignment > maxalign) {
239 maxalign = type->alignment;
240 }
241
242 if ((padding = psi_offset_padding(darg->layout->pos - offset, type->alignment))) {
243 if (nels + padding + 1 > argc) {
244 argc += padding;
245 els = realloc(els, (argc + 1) * sizeof(*els));
246 els[argc] = NULL;
247 }
248 psi_ffi_struct_type_pad(&els[nels], padding);
249 nels += padding;
250 offset += padding;
251 }
252 ZEND_ASSERT(offset == darg->layout->pos);
253
254 offset = (offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
255 els[nels++] = type;
256 }
257
258 /* apply struct alignment padding */
259 offset = (offset + maxalign - 1) & ~(maxalign - 1);
260
261 ZEND_ASSERT(offset <= strct->size);
262 if (offset < strct->size) {
263 psi_ffi_struct_type_pad(&els[nels], strct->size - offset);
264 }
265
266 return els;
267 }
268 static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
269 decl_type *real = real_decl_type(type);
270
271 switch (real->type) {
272 case PSI_T_STRUCT:
273 if (!real->real.strct->engine.type) {
274 ffi_type *strct = calloc(1, sizeof(ffi_type));
275
276 strct->type = FFI_TYPE_STRUCT;
277 strct->size = 0;
278 strct->elements = psi_ffi_struct_type_elements(real->real.strct);
279
280 real->real.strct->engine.type = strct;
281 real->real.strct->engine.dtor = psi_ffi_struct_type_dtor;
282 }
283
284 return real->real.strct->engine.type;
285
286 case PSI_T_UNION:
287 return psi_ffi_decl_arg_type(real->real.unn->args->args[0]);
288
289 default:
290 return psi_ffi_token_type(real->type);
291 }
292 }
293 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
294 if (darg->var->pointer_level) {
295 return &ffi_type_pointer;
296 } else {
297 return psi_ffi_decl_type(darg->type);
298 }
299 }
300
301
302 static inline struct psi_ffi_context *psi_ffi_context_init(struct psi_ffi_context *L) {
303 ffi_status rc;
304
305 if (!L) {
306 L = malloc(sizeof(*L));
307 }
308 memset(L, 0, sizeof(*L));
309
310 L->params[0] = &ffi_type_pointer;
311 L->params[1] = &ffi_type_pointer;
312 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
313 ZEND_ASSERT(rc == FFI_OK);
314
315 return L;
316 }
317
318 static inline void psi_ffi_context_free(struct psi_ffi_context **L) {
319 if (*L) {
320 free(*L);
321 *L = NULL;
322 }
323 }
324
325 static void psi_ffi_init(struct psi_context *C)
326 {
327 C->context = psi_ffi_context_init(NULL);
328 }
329
330 static inline void psi_ffi_destroy_callbacks(struct psi_context *C, let_val *let_val) {
331 let_callback *cb;
332 let_func *fn = NULL;
333
334 switch (let_val->kind) {
335 case PSI_LET_CALLBACK:
336 cb = let_val->data.callback;
337
338 if (cb->decl && cb->decl->call.info) {
339 psi_ffi_call_free(cb->decl->call.info);
340 }
341 fn = cb->func;
342 /* no break */
343 case PSI_LET_FUNC:
344 if (!fn) {
345 fn = let_val->data.func;
346 }
347
348 if (fn->inner) {
349 size_t i;
350
351 for (i = 0; i < fn->inner->count; ++i) {
352 psi_ffi_destroy_callbacks(C, fn->inner->vals[i]);
353 }
354 }
355 break;
356 default:
357 break;
358 }
359 }
360
361 static void psi_ffi_dtor(struct psi_context *C)
362 {
363 if (C->decls) {
364 size_t i;
365
366 for (i = 0; i < C->decls->count; ++i) {
367 decl *decl = C->decls->list[i];
368
369 if (decl->call.info) {
370 psi_ffi_call_free(decl->call.info);
371 }
372 }
373
374 }
375 if (C->impls) {
376 size_t i, j;
377
378 for (i = 0; i < C->impls->count; ++i) {
379 impl *impl = C->impls->list[i];
380
381 for (j = 0; j < impl->stmts->let.count; ++j) {
382 let_stmt *let = impl->stmts->let.list[j];
383
384 if (let->val && let->val->kind == PSI_LET_CALLBACK) {
385 let_callback *cb = let->val->data.callback;
386
387 if (cb->decl && cb->decl->call.info) {
388 psi_ffi_call_free(cb->decl->call.info);
389 }
390 }
391 }
392 }
393 }
394 psi_ffi_context_free((void *) &C->context);
395 }
396
397 static inline void psi_ffi_compile_callbacks(struct psi_context *C, let_val *let_val) {
398 struct psi_ffi_call *call;
399 let_callback *cb;
400 let_func *fn = NULL;
401
402 switch (let_val->kind) {
403 case PSI_LET_CALLBACK:
404 cb = let_val->data.callback;
405 if ((call = psi_ffi_call_alloc(C, cb->decl))) {
406 if (FFI_OK != psi_ffi_call_init_callback_closure(C, call, cb)) {
407 psi_ffi_call_free(call);
408 break;
409 }
410
411 cb->decl->call.sym = call->code;
412 }
413 fn = cb->func;
414 /* no break */
415 case PSI_LET_FUNC:
416 if (!fn) {
417 fn = let_val->data.func;
418 }
419 if (fn->inner) {
420 size_t i;
421
422 for (i = 0; i < fn->inner->count; ++i) {
423 psi_ffi_compile_callbacks(C, fn->inner->vals[i]);
424 }
425 }
426 break;
427 default:
428 break;
429 }
430 }
431
432 static zend_function_entry *psi_ffi_compile(struct psi_context *C)
433 {
434 size_t c, i, j = 0;
435 zend_function_entry *zfe;
436
437 if (!C->impls) {
438 return NULL;
439 }
440
441 zfe = calloc(C->impls->count + 1, sizeof(*zfe));
442 for (i = 0; i < C->impls->count; ++i) {
443 zend_function_entry *zf = &zfe[j];
444 struct psi_ffi_call *call;
445 impl *impl = C->impls->list[i];
446
447 if (!impl->decl) {
448 continue;
449 }
450
451 if ((call = psi_ffi_call_alloc(C, impl->decl))) {
452 if (FFI_OK != psi_ffi_call_init_closure(C, call, impl)) {
453 psi_ffi_call_free(call);
454 continue;
455 }
456 }
457
458 zf->fname = impl->func->name + (impl->func->name[0] == '\\');
459 zf->num_args = impl->func->args->count;
460 zf->handler = call->code;
461 zf->arg_info = psi_internal_arginfo(impl);
462 ++j;
463
464 for (c = 0; c < impl->stmts->let.count; ++c) {
465 psi_ffi_compile_callbacks(C, impl->stmts->let.list[c]->val);
466 }
467 }
468
469 for (i = 0; i < C->decls->count; ++i) {
470 decl *decl = C->decls->list[i];
471
472 if (decl->call.info) {
473 continue;
474 }
475
476 psi_ffi_call_alloc(C, decl);
477 }
478
479 return zfe;
480 }
481
482 static void psi_ffi_call(struct psi_context *C, decl_callinfo *decl_call, impl_vararg *va) {
483 struct psi_ffi_call *call = decl_call->info;
484
485 if (va) {
486 ffi_status rc;
487 ffi_cif signature;
488 size_t i, nfixedargs = decl_call->argc, ntotalargs = nfixedargs + va->args->count;
489 void **params = calloc(2 * ntotalargs + 2, sizeof(void *));
490
491 for (i = 0; i < nfixedargs; ++i) {
492 params[i] = call->params[i];
493 params[i + ntotalargs + 1] = call->params[i + nfixedargs + 1];
494 }
495 for (i = 0; i < va->args->count; ++i) {
496 params[nfixedargs + i] = psi_ffi_impl_type(va->types[i]);
497 params[nfixedargs + i + ntotalargs + 1] = &va->values[i];
498 }
499 #ifdef PSI_HAVE_FFI_PREP_CIF_VAR
500 rc = ffi_prep_cif_var(&signature, call->signature.abi,
501 nfixedargs, ntotalargs,
502 call->signature.rtype, (ffi_type **) params);
503 #else
504 /* FIXME: test in config.m4; assume we can just call anyway */
505 rc = ffi_prep_cif(&signature, call->signature.abi, ntotalargs,
506 call->signature.rtype, (ffi_type **) params);
507 #endif
508 ZEND_ASSERT(FFI_OK == rc);
509 ffi_call(&signature, FFI_FN(decl_call->sym), *decl_call->rval, &params[ntotalargs + 1]);
510 free(params);
511 } else {
512 ffi_call(&call->signature, FFI_FN(decl_call->sym), *decl_call->rval, decl_call->args);
513 }
514 }
515
516 static struct psi_context_ops ops = {
517 psi_ffi_init,
518 psi_ffi_dtor,
519 psi_ffi_compile,
520 psi_ffi_call,
521 };
522
523 struct psi_context_ops *psi_libffi_ops(void)
524 {
525 return &ops;
526 }
527
528 #endif /* HAVE_LIBFFI */