1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
34 #undef PACKAGE_BUGREPORT
37 #undef PACKAGE_TARNAME
38 #undef PACKAGE_VERSION
42 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
47 # include <sys/mman.h>
48 # ifndef MAP_ANONYMOUS
49 # define MAP_ANONYMOUS MAP_ANON
54 static void *psi_ffi_closure_alloc(size_t s
, void **code
)
56 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
57 return ffi_closure_alloc(s
, code
);
59 *code
= mmap(NULL
, s
, PROT_EXEC
|PROT_WRITE
|PROT_READ
,
60 MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
61 if (MAP_FAILED
== *code
) {
66 # error "Neither ffi_closure_alloc() nor mmap() available"
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
);
74 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
75 return ffi_prep_closure_loc(*closure
, sig
, handler
, data
, *code
);
77 #elif PSI_HAVE_FFI_PREP_CLOSURE
78 return ffi_prep_closure(*code
, sig
, handler
, data
);
80 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
84 static void psi_ffi_closure_free(void *c
)
86 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
89 munmap(c
, sizeof(ffi_closure
));
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
) {
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
);
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
);
105 assert(FFI_OK
== rc
);
108 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
);
110 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
116 return &ffi_type_void
;
118 return &ffi_type_sint8
;
120 return &ffi_type_uint8
;
122 return &ffi_type_sint16
;
124 return &ffi_type_uint16
;
126 return &ffi_type_sint32
;
128 return &ffi_type_uint32
;
130 return &ffi_type_sint64
;
132 return &ffi_type_uint64
;
134 return &ffi_type_uchar
;
137 return &ffi_type_sint
;
139 return &ffi_type_slong
;
141 return &ffi_type_float
;
143 return &ffi_type_double
;
144 #ifdef HAVE_LONG_DOUBLE
145 case PSI_T_LONG_DOUBLE
:
146 return &ffi_type_longdouble
;
150 return &ffi_type_pointer
;
153 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
156 return &ffi_type_sint8
;
158 return &ffi_type_sint64
;
160 return &ffi_type_pointer
;
163 return &ffi_type_double
;
164 EMPTY_SWITCH_DEFAULT_CASE();
168 static void psi_ffi_struct_type_dtor(void *type
) {
169 ffi_type
*strct
= type
;
171 if (strct
->elements
) {
174 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
177 free(strct
->elements
);
182 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
185 for (i
= 0; i
< padding
; ++i
) {
186 ffi_type
*pad
= malloc(sizeof(*pad
));
188 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
195 static ffi_type
**psi_ffi_struct_type_elements(struct psi_decl_struct
*strct
) {
196 size_t i
= 0, argc
, nels
= 0, offset
= 0, maxalign
= 0, last_arg_pos
= -1;
197 ffi_type
**tmp
, **els
;
198 struct psi_decl_arg
*darg
;
200 argc
= psi_plist_count(strct
->args
);
201 els
= calloc(argc
+ 1, sizeof(*els
));
203 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
207 if (darg
->layout
->pos
== last_arg_pos
) {
208 /* skip bit fields */
211 last_arg_pos
= darg
->layout
->pos
;
213 type
= malloc(sizeof(*type
));
214 *type
= *psi_ffi_decl_arg_type(darg
);
216 if (type
->alignment
> maxalign
) {
217 maxalign
= type
->alignment
;
220 assert(type
->size
<= darg
->layout
->len
);
221 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
222 if (nels
+ padding
+ 1 > argc
) {
224 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
233 psi_ffi_struct_type_pad(&els
[nels
], padding
);
237 assert(offset
== darg
->layout
->pos
);
239 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
243 /* apply struct alignment padding */
244 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
246 assert(offset
<= strct
->size
);
247 if (offset
< strct
->size
) {
248 size_t padding
= strct
->size
- offset
;
250 tmp
= realloc(els
, (padding
+ argc
+ 1) * sizeof(*els
));
257 psi_ffi_struct_type_pad(&els
[nels
], padding
);
258 els
[argc
+ padding
] = NULL
;
263 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
264 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
266 switch (real
->type
) {
268 if (!real
->real
.strct
->engine
.type
) {
269 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
271 strct
->type
= FFI_TYPE_STRUCT
;
273 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
275 real
->real
.strct
->engine
.type
= strct
;
276 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
279 return real
->real
.strct
->engine
.type
;
283 struct psi_decl_arg
*arg
;
284 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
285 return psi_ffi_decl_arg_type(arg
);
289 return psi_ffi_token_type(real
->type
);
292 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
293 if (darg
->var
->pointer_level
) {
294 return &ffi_type_pointer
;
296 return psi_ffi_decl_type(darg
->type
);
300 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
301 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
302 #ifdef HAVE_FFI_STDCALL
303 if (!strcasecmp(convention
, "stdcall")) {
307 #ifdef HAVE_FFI_FASTCALL
308 if (!strcasecmp(convention
, "fastcall")) {
313 return FFI_DEFAULT_ABI
;
316 struct psi_ffi_context
{
321 struct psi_ffi_impl_info
{
322 struct psi_context
*context
;
323 struct psi_call_frame
*frame
;
326 ffi_closure
*closure
;
329 struct psi_ffi_callback_info
{
330 struct psi_ffi_impl_info
*impl_info
;
331 struct psi_let_exp
*let_exp
;
334 ffi_closure
*closure
;
337 struct psi_ffi_decl_info
{
342 static inline struct psi_ffi_decl_info
*psi_ffi_decl_init(struct psi_decl
*decl
) {
345 size_t i
, c
= psi_plist_count(decl
->args
);
346 struct psi_decl_arg
*arg
;
347 struct psi_ffi_decl_info
*info
= calloc(1, sizeof(*info
) + 2 * c
* sizeof(void *));
349 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
350 info
->params
[i
] = psi_ffi_decl_arg_type(arg
);
352 info
->params
[c
] = NULL
;
354 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
355 c
, psi_ffi_decl_arg_type(decl
->func
), info
->params
);
367 static inline void psi_ffi_decl_dtor(struct psi_decl
*decl
) {
374 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
376 struct psi_impl
*impl
= data
;
377 struct psi_ffi_impl_info
*info
= impl
->info
;
379 psi_context_call(info
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], impl
);
382 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
384 struct psi_ffi_callback_info
*cb_info
= data
;
385 struct psi_call_frame_callback cb_data
;
387 assert(cb_info
->impl_info
->frame
);
389 cb_data
.cb
= cb_info
->let_exp
;
390 cb_data
.argc
= sig
->nargs
;
392 cb_data
.rval
= result
;
394 psi_call_frame_do_callback(cb_info
->impl_info
->frame
, &cb_data
);
397 static inline void psi_ffi_callback_init(struct psi_ffi_impl_info
*impl_info
,
398 struct psi_let_exp
*let_exp
) {
399 struct psi_ffi_callback_info
*cb_info
;
400 struct psi_ffi_decl_info
*decl_info
;
401 struct psi_let_callback
*cb
;
402 struct psi_let_func
*fn
= NULL
;
405 switch (let_exp
->kind
) {
406 case PSI_LET_CALLBACK
:
407 cb
= let_exp
->data
.callback
;
408 if (cb
->decl
->info
) {
409 decl_info
= cb
->decl
->info
;
411 decl_info
= psi_ffi_decl_init(cb
->decl
);
414 cb_info
= calloc(1, sizeof(*cb_info
));
415 cb_info
->impl_info
= impl_info
;
416 cb_info
->let_exp
= let_exp
;
417 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
418 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
426 assert(!cb
->decl
->sym
);
427 cb
->decl
->sym
= cb_info
->code
;
433 fn
= let_exp
->data
.func
;
437 struct psi_let_exp
*inner_let
;
439 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
440 psi_ffi_callback_init(impl_info
, inner_let
);
449 static inline void psi_ffi_callback_dtor(struct psi_let_exp
*let_exp
) {
450 struct psi_let_callback
*cb
;
451 struct psi_let_func
*fn
= NULL
;
453 switch (let_exp
->kind
) {
454 case PSI_LET_CALLBACK
:
455 cb
= let_exp
->data
.callback
;
457 psi_ffi_decl_dtor(cb
->decl
);
460 struct psi_ffi_callback_info
*info
= cb
->info
;
463 psi_ffi_closure_free(info
->closure
);
472 fn
= let_exp
->data
.func
;
477 struct psi_let_exp
*cb
;
479 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
480 psi_ffi_callback_dtor(cb
);
489 static inline struct psi_ffi_impl_info
*psi_ffi_impl_init(struct psi_impl
*impl
,
490 struct psi_context
*C
) {
491 struct psi_ffi_context
*context
= C
->context
;
492 struct psi_ffi_impl_info
*info
= calloc(1, sizeof(*info
));
493 struct psi_let_stmt
*let
;
499 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
500 &context
->signature
, psi_ffi_handler
, impl
);
507 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
508 psi_ffi_callback_init(info
, let
->exp
);
511 return impl
->info
= info
;
514 static inline void psi_ffi_impl_dtor(struct psi_impl
*impl
) {
515 struct psi_ffi_impl_info
*info
= impl
->info
;
516 struct psi_let_stmt
*let
;
519 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
520 psi_ffi_callback_dtor(let
->exp
);
525 psi_ffi_closure_free(info
->closure
);
533 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
537 L
= malloc(sizeof(*L
));
539 memset(L
, 0, sizeof(*L
));
541 L
->params
[0] = &ffi_type_pointer
;
542 L
->params
[1] = &ffi_type_pointer
;
543 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
544 assert(rc
== FFI_OK
);
549 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
556 static void psi_ffi_init(struct psi_context
*C
)
558 C
->context
= psi_ffi_context_init(NULL
);
561 static void psi_ffi_dtor(struct psi_context
*C
)
565 struct psi_decl
*decl
;
567 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
568 psi_ffi_decl_dtor(decl
);
574 struct psi_impl
*impl
;
576 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
577 psi_ffi_impl_dtor(impl
);
580 psi_ffi_context_free((void *) &C
->context
);
584 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
586 size_t i
= 0, d
= 0, nf
= 0;
587 struct psi_impl
*impl
;
588 struct psi_decl
*decl
;
589 zend_function_entry
*zfe
;
595 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
597 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
598 zend_function_entry
*zf
= &zfe
[nf
];
603 if (!psi_ffi_decl_init(impl
->decl
)) {
606 if (!psi_ffi_impl_init(impl
, C
)) {
610 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
611 zf
->handler
= ((struct psi_ffi_impl_info
*) impl
->info
)->code
;
612 zf
->num_args
= psi_plist_count(impl
->func
->args
);
613 zf
->arg_info
= psi_internal_arginfo(impl
);
617 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
622 psi_ffi_decl_init(decl
);
628 static inline void psi_ffi_call_ex(struct psi_call_frame
*frame
) {
629 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
630 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
631 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
632 struct psi_ffi_impl_info
*impl_info
;
633 struct psi_call_frame
*prev
;
636 impl_info
= impl
->info
;
637 prev
= impl_info
->frame
;
638 impl_info
->frame
= frame
;
640 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
641 psi_call_frame_get_rpointer(frame
),
642 psi_call_frame_get_arg_pointers(frame
));
644 impl_info
->frame
= prev
;
648 static inline void psi_ffi_call_va(struct psi_call_frame
*frame
) {
650 struct psi_call_frame
*prev
;
651 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
652 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
653 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
654 struct psi_ffi_impl_info
*impl_info
;
655 size_t i
, va_count
, argc
;
656 ffi_type
**param_types
;
658 argc
= psi_plist_count(decl
->args
);
659 va_count
= psi_call_frame_num_var_args(frame
);
660 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
661 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
662 for (i
= 0; i
< va_count
; ++i
) {
663 struct psi_call_frame_argument
*frame_arg
;
665 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
666 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
669 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
672 impl_info
= impl
->info
;
673 prev
= impl_info
->frame
;
674 impl_info
->frame
= frame
;
676 ffi_call(&signature
, FFI_FN(decl
->sym
),
677 psi_call_frame_get_rpointer(frame
),
678 psi_call_frame_get_arg_pointers(frame
));
680 impl_info
->frame
= prev
;
686 static void psi_ffi_call(struct psi_call_frame
*frame
) {
687 if (psi_call_frame_num_var_args(frame
)) {
688 psi_ffi_call_va(frame
);
690 psi_ffi_call_ex(frame
);
694 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
696 case PSI_CONTEXT_QUERY_SELF
:
698 case PSI_CONTEXT_QUERY_TYPE
:
699 return psi_ffi_impl_type(*(token_t
*) arg
);
704 static struct psi_context_ops ops
= {
712 struct psi_context_ops
*psi_libffi_ops(void)
717 #endif /* HAVE_LIBFFI */