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
);
109 static ffi_type
*ffi_type_sint128
;
110 static ffi_type
*ffi_type_uint128
;
113 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
);
115 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
121 return &ffi_type_void
;
123 return &ffi_type_sint8
;
125 return &ffi_type_uint8
;
127 return &ffi_type_sint16
;
129 return &ffi_type_uint16
;
131 return &ffi_type_sint32
;
133 return &ffi_type_uint32
;
135 return &ffi_type_sint64
;
137 return &ffi_type_uint64
;
140 return ffi_type_sint128
;
142 return ffi_type_uint128
;
145 return &ffi_type_uchar
;
147 return &ffi_type_sint
;
149 return &ffi_type_float
;
151 return &ffi_type_double
;
152 #ifdef HAVE_LONG_DOUBLE
153 case PSI_T_LONG_DOUBLE
:
154 return &ffi_type_longdouble
;
158 return &ffi_type_pointer
;
161 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
164 return &ffi_type_sint8
;
166 return &ffi_type_sint64
;
168 return &ffi_type_pointer
;
171 return &ffi_type_double
;
172 EMPTY_SWITCH_DEFAULT_CASE();
176 static void psi_ffi_struct_type_dtor(void *type
) {
177 ffi_type
*strct
= type
;
179 if (strct
->elements
) {
182 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
185 free(strct
->elements
);
190 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
193 for (i
= 0; i
< padding
; ++i
) {
194 ffi_type
*pad
= malloc(sizeof(*pad
));
196 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
203 static ffi_type
**psi_ffi_struct_type_elements(struct psi_decl_struct
*strct
) {
204 size_t i
= 0, argc
, nels
= 0, offset
= 0, maxalign
= 0, last_arg_pos
= -1;
205 ffi_type
**tmp
, **els
;
206 struct psi_decl_arg
*darg
;
208 argc
= psi_plist_count(strct
->args
);
209 els
= calloc(argc
+ 1, sizeof(*els
));
211 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
215 if (darg
->layout
->pos
== last_arg_pos
) {
216 /* skip bit fields */
219 last_arg_pos
= darg
->layout
->pos
;
221 type
= malloc(sizeof(*type
));
222 *type
= *psi_ffi_decl_arg_type(darg
);
224 if (type
->alignment
> maxalign
) {
225 maxalign
= type
->alignment
;
228 assert(type
->size
<= darg
->layout
->len
);
229 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
230 if (nels
+ padding
+ 1 > argc
) {
232 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
241 psi_ffi_struct_type_pad(&els
[nels
], padding
);
245 assert(offset
== darg
->layout
->pos
);
247 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
251 /* apply struct alignment padding */
252 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
254 assert(offset
<= strct
->size
);
255 if (offset
< strct
->size
) {
256 size_t padding
= strct
->size
- offset
;
258 tmp
= realloc(els
, (padding
+ argc
+ 1) * sizeof(*els
));
265 psi_ffi_struct_type_pad(&els
[nels
], padding
);
266 els
[argc
+ padding
] = NULL
;
271 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
272 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
274 switch (real
->type
) {
276 if (!real
->real
.strct
->engine
.type
) {
277 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
279 strct
->type
= FFI_TYPE_STRUCT
;
281 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
283 real
->real
.strct
->engine
.type
= strct
;
284 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
287 return real
->real
.strct
->engine
.type
;
291 struct psi_decl_arg
*arg
;
292 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
293 return psi_ffi_decl_arg_type(arg
);
297 return psi_ffi_token_type(real
->type
);
300 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
301 if (darg
->var
->pointer_level
) {
302 return &ffi_type_pointer
;
304 return psi_ffi_decl_type(darg
->type
);
308 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
309 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
310 #ifdef HAVE_FFI_STDCALL
311 if (!strcasecmp(convention
, "stdcall")) {
315 #ifdef HAVE_FFI_FASTCALL
316 if (!strcasecmp(convention
, "fastcall")) {
321 return FFI_DEFAULT_ABI
;
324 struct psi_ffi_context
{
329 struct psi_ffi_impl_info
{
330 struct psi_context
*context
;
331 struct psi_call_frame
*frame
;
334 ffi_closure
*closure
;
337 struct psi_ffi_callback_info
{
338 struct psi_ffi_impl_info
*impl_info
;
339 struct psi_let_exp
*let_exp
;
342 ffi_closure
*closure
;
345 struct psi_ffi_decl_info
{
350 static inline struct psi_ffi_decl_info
*psi_ffi_decl_init(struct psi_decl
*decl
) {
353 size_t i
, c
= psi_plist_count(decl
->args
);
354 struct psi_decl_arg
*arg
;
355 struct psi_ffi_decl_info
*info
= calloc(1, sizeof(*info
) + 2 * c
* sizeof(void *));
357 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
358 info
->params
[i
] = psi_ffi_decl_arg_type(arg
);
360 info
->params
[c
] = NULL
;
362 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
363 c
, psi_ffi_decl_arg_type(decl
->func
), info
->params
);
375 static inline void psi_ffi_decl_dtor(struct psi_decl
*decl
) {
382 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
384 struct psi_impl
*impl
= data
;
385 struct psi_ffi_impl_info
*info
= impl
->info
;
387 psi_context_call(info
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], impl
);
390 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
392 struct psi_ffi_callback_info
*cb_info
= data
;
393 struct psi_call_frame_callback cb_data
;
395 assert(cb_info
->impl_info
->frame
);
397 cb_data
.cb
= cb_info
->let_exp
;
398 cb_data
.argc
= sig
->nargs
;
400 cb_data
.rval
= result
;
402 psi_call_frame_do_callback(cb_info
->impl_info
->frame
, &cb_data
);
405 static inline void psi_ffi_callback_init(struct psi_ffi_impl_info
*impl_info
,
406 struct psi_let_exp
*let_exp
) {
407 struct psi_ffi_callback_info
*cb_info
;
408 struct psi_ffi_decl_info
*decl_info
;
409 struct psi_let_callback
*cb
;
410 struct psi_let_func
*fn
= NULL
;
413 switch (let_exp
->kind
) {
414 case PSI_LET_CALLBACK
:
415 cb
= let_exp
->data
.callback
;
416 if (cb
->decl
->info
) {
417 decl_info
= cb
->decl
->info
;
419 decl_info
= psi_ffi_decl_init(cb
->decl
);
422 cb_info
= calloc(1, sizeof(*cb_info
));
423 cb_info
->impl_info
= impl_info
;
424 cb_info
->let_exp
= let_exp
;
425 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
426 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
434 assert(!cb
->decl
->sym
);
435 cb
->decl
->sym
= cb_info
->code
;
441 fn
= let_exp
->data
.func
;
445 struct psi_let_exp
*inner_let
;
447 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
448 psi_ffi_callback_init(impl_info
, inner_let
);
457 static inline void psi_ffi_callback_dtor(struct psi_let_exp
*let_exp
) {
458 struct psi_let_callback
*cb
;
459 struct psi_let_func
*fn
= NULL
;
461 switch (let_exp
->kind
) {
462 case PSI_LET_CALLBACK
:
463 cb
= let_exp
->data
.callback
;
465 psi_ffi_decl_dtor(cb
->decl
);
468 struct psi_ffi_callback_info
*info
= cb
->info
;
471 psi_ffi_closure_free(info
->closure
);
480 fn
= let_exp
->data
.func
;
485 struct psi_let_exp
*cb
;
487 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
488 psi_ffi_callback_dtor(cb
);
497 static inline struct psi_ffi_impl_info
*psi_ffi_impl_init(struct psi_impl
*impl
,
498 struct psi_context
*C
) {
499 struct psi_ffi_context
*context
= C
->context
;
500 struct psi_ffi_impl_info
*info
= calloc(1, sizeof(*info
));
501 struct psi_let_stmt
*let
;
507 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
508 &context
->signature
, psi_ffi_handler
, impl
);
515 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
516 psi_ffi_callback_init(info
, let
->exp
);
519 return impl
->info
= info
;
522 static inline void psi_ffi_impl_dtor(struct psi_impl
*impl
) {
523 struct psi_ffi_impl_info
*info
= impl
->info
;
524 struct psi_let_stmt
*let
;
527 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
528 psi_ffi_callback_dtor(let
->exp
);
533 psi_ffi_closure_free(info
->closure
);
540 static void psi_ffi_extvar_get(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
541 struct psi_decl_extvar
*evar
= data
;
543 psi_decl_extvar_get(evar
, result
);
546 static void psi_ffi_extvar_set(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
547 struct psi_decl_extvar
*evar
= data
;
549 psi_decl_extvar_set(evar
, args
[0]);
552 struct psi_ffi_extvar_info
{
556 ffi_closure
*closure
;
562 ffi_closure
*closure
;
566 static inline ffi_status
psi_ffi_extvar_init(struct psi_decl_extvar
*evar
,
568 struct psi_ffi_extvar_info
*info
= calloc(1, sizeof(*info
));
571 rc
= ffi_prep_cif(&info
->get
.signature
, FFI_DEFAULT_ABI
, 0,
576 rc
= psi_ffi_prep_closure(&info
->get
.closure
, &info
->get
.code
,
577 &info
->get
.signature
, psi_ffi_extvar_get
, evar
);
582 info
->set
.params
[0] = type
;
583 rc
= ffi_prep_cif(&info
->set
.signature
, FFI_DEFAULT_ABI
, 1,
584 &ffi_type_void
, info
->set
.params
);
588 rc
= psi_ffi_prep_closure(&info
->set
.closure
, &info
->set
.code
,
589 &info
->set
.signature
, psi_ffi_extvar_set
, evar
);
595 evar
->getter
->sym
= info
->get
.code
;
596 evar
->setter
->sym
= info
->set
.code
;
601 static inline void psi_ffi_extvar_dtor(struct psi_decl_extvar
*evar
) {
608 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
612 L
= malloc(sizeof(*L
));
614 memset(L
, 0, sizeof(*L
));
616 L
->params
[0] = &ffi_type_pointer
;
617 L
->params
[1] = &ffi_type_pointer
;
618 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
619 assert(rc
== FFI_OK
);
624 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
631 static void psi_ffi_init(struct psi_context
*C
)
633 C
->context
= psi_ffi_context_init(NULL
);
636 static void psi_ffi_dtor(struct psi_context
*C
)
640 struct psi_decl
*decl
;
642 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
643 psi_ffi_decl_dtor(decl
);
649 struct psi_decl_extvar
*evar
;
651 while (psi_plist_get(C
->vars
, i
++, &evar
)) {
652 psi_ffi_extvar_dtor(evar
);
657 struct psi_impl
*impl
;
659 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
660 psi_ffi_impl_dtor(impl
);
663 psi_ffi_context_free((void *) &C
->context
);
667 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
669 size_t i
= 0, d
= 0, v
= 0, nf
= 0;
670 struct psi_impl
*impl
;
671 struct psi_decl
*decl
;
672 struct psi_decl_extvar
*evar
;
673 zend_function_entry
*zfe
= NULL
;
675 while (psi_plist_get(C
->vars
, v
++, &evar
)) {
676 ffi_type
*type
= psi_ffi_decl_arg_type(evar
->arg
);
678 if (FFI_OK
== psi_ffi_extvar_init(evar
, type
)) {
684 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
686 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
687 zend_function_entry
*zf
= &zfe
[nf
];
692 if (!psi_ffi_decl_init(impl
->decl
)) {
695 if (!psi_ffi_impl_init(impl
, C
)) {
699 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
700 zf
->handler
= ((struct psi_ffi_impl_info
*) impl
->info
)->code
;
701 zf
->num_args
= psi_plist_count(impl
->func
->args
);
702 zf
->arg_info
= psi_internal_arginfo(impl
);
707 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
712 psi_ffi_decl_init(decl
);
718 static inline void psi_ffi_call_ex(struct psi_call_frame
*frame
) {
719 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
720 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
721 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
722 struct psi_ffi_impl_info
*impl_info
;
723 struct psi_call_frame
*prev
;
726 impl_info
= impl
->info
;
727 prev
= impl_info
->frame
;
728 impl_info
->frame
= frame
;
730 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
731 psi_call_frame_get_rpointer(frame
),
732 psi_call_frame_get_arg_pointers(frame
));
734 impl_info
->frame
= prev
;
738 static inline void psi_ffi_call_va(struct psi_call_frame
*frame
) {
740 struct psi_call_frame
*prev
;
741 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
742 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
743 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
744 struct psi_ffi_impl_info
*impl_info
;
745 size_t i
, va_count
, argc
;
746 ffi_type
**param_types
;
748 argc
= psi_plist_count(decl
->args
);
749 va_count
= psi_call_frame_num_var_args(frame
);
750 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
751 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
752 for (i
= 0; i
< va_count
; ++i
) {
753 struct psi_call_frame_argument
*frame_arg
;
755 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
756 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
759 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
762 impl_info
= impl
->info
;
763 prev
= impl_info
->frame
;
764 impl_info
->frame
= frame
;
766 ffi_call(&signature
, FFI_FN(decl
->sym
),
767 psi_call_frame_get_rpointer(frame
),
768 psi_call_frame_get_arg_pointers(frame
));
770 impl_info
->frame
= prev
;
776 static void psi_ffi_call(struct psi_call_frame
*frame
) {
777 if (psi_call_frame_num_var_args(frame
)) {
778 psi_ffi_call_va(frame
);
780 psi_ffi_call_ex(frame
);
784 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
786 case PSI_CONTEXT_QUERY_SELF
:
788 case PSI_CONTEXT_QUERY_TYPE
:
789 return psi_ffi_impl_type(*(token_t
*) arg
);
794 static ZEND_RESULT_CODE
psi_ffi_load()
797 ffi_type
*i128
, *u128
;
799 i128
= calloc(1, 3*sizeof(ffi_type
));
800 i128
->type
= FFI_TYPE_STRUCT
;
802 i128
->elements
= (ffi_type
**) (i128
+ 1);
803 i128
->elements
[0] = &ffi_type_sint64
;
804 i128
->elements
[1] = &ffi_type_sint64
;
806 ffi_type_sint128
= i128
;
808 u128
= calloc(1, 3*sizeof(ffi_type
));
809 u128
->type
= FFI_TYPE_STRUCT
;
811 u128
->elements
= (ffi_type
**) (u128
+ 1);
812 u128
->elements
[0] = &ffi_type_uint64
;
813 u128
->elements
[1] = &ffi_type_uint64
;
815 ffi_type_uint128
= u128
;
820 static void psi_ffi_free()
823 free(ffi_type_sint128
);
824 free(ffi_type_uint128
);
828 static struct psi_context_ops ops
= {
838 struct psi_context_ops
*psi_libffi_ops(void)
843 #endif /* HAVE_LIBFFI */