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
);
541 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
545 L
= malloc(sizeof(*L
));
547 memset(L
, 0, sizeof(*L
));
549 L
->params
[0] = &ffi_type_pointer
;
550 L
->params
[1] = &ffi_type_pointer
;
551 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
552 assert(rc
== FFI_OK
);
557 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
564 static void psi_ffi_init(struct psi_context
*C
)
566 C
->context
= psi_ffi_context_init(NULL
);
569 static void psi_ffi_dtor(struct psi_context
*C
)
573 struct psi_decl
*decl
;
575 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
576 psi_ffi_decl_dtor(decl
);
582 struct psi_impl
*impl
;
584 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
585 psi_ffi_impl_dtor(impl
);
588 psi_ffi_context_free((void *) &C
->context
);
592 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
594 size_t i
= 0, d
= 0, nf
= 0;
595 struct psi_impl
*impl
;
596 struct psi_decl
*decl
;
597 zend_function_entry
*zfe
;
603 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
605 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
606 zend_function_entry
*zf
= &zfe
[nf
];
611 if (!psi_ffi_decl_init(impl
->decl
)) {
614 if (!psi_ffi_impl_init(impl
, C
)) {
618 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
619 zf
->handler
= ((struct psi_ffi_impl_info
*) impl
->info
)->code
;
620 zf
->num_args
= psi_plist_count(impl
->func
->args
);
621 zf
->arg_info
= psi_internal_arginfo(impl
);
625 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
630 psi_ffi_decl_init(decl
);
636 static inline void psi_ffi_call_ex(struct psi_call_frame
*frame
) {
637 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
638 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
639 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
640 struct psi_ffi_impl_info
*impl_info
;
641 struct psi_call_frame
*prev
;
644 impl_info
= impl
->info
;
645 prev
= impl_info
->frame
;
646 impl_info
->frame
= frame
;
648 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
649 psi_call_frame_get_rpointer(frame
),
650 psi_call_frame_get_arg_pointers(frame
));
652 impl_info
->frame
= prev
;
656 static inline void psi_ffi_call_va(struct psi_call_frame
*frame
) {
658 struct psi_call_frame
*prev
;
659 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
660 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
661 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
662 struct psi_ffi_impl_info
*impl_info
;
663 size_t i
, va_count
, argc
;
664 ffi_type
**param_types
;
666 argc
= psi_plist_count(decl
->args
);
667 va_count
= psi_call_frame_num_var_args(frame
);
668 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
669 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
670 for (i
= 0; i
< va_count
; ++i
) {
671 struct psi_call_frame_argument
*frame_arg
;
673 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
674 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
677 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
680 impl_info
= impl
->info
;
681 prev
= impl_info
->frame
;
682 impl_info
->frame
= frame
;
684 ffi_call(&signature
, FFI_FN(decl
->sym
),
685 psi_call_frame_get_rpointer(frame
),
686 psi_call_frame_get_arg_pointers(frame
));
688 impl_info
->frame
= prev
;
694 static void psi_ffi_call(struct psi_call_frame
*frame
) {
695 if (psi_call_frame_num_var_args(frame
)) {
696 psi_ffi_call_va(frame
);
698 psi_ffi_call_ex(frame
);
702 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
704 case PSI_CONTEXT_QUERY_SELF
:
706 case PSI_CONTEXT_QUERY_TYPE
:
707 return psi_ffi_impl_type(*(token_t
*) arg
);
712 static ZEND_RESULT_CODE
psi_ffi_load()
715 ffi_type
*i128
, *u128
;
717 i128
= calloc(1, 3*sizeof(ffi_type
));
718 i128
->type
= FFI_TYPE_STRUCT
;
720 i128
->elements
= (ffi_type
**) (i128
+ 1);
721 i128
->elements
[0] = &ffi_type_sint64
;
722 i128
->elements
[1] = &ffi_type_sint64
;
724 ffi_type_sint128
= i128
;
726 u128
= calloc(1, 3*sizeof(ffi_type
));
727 u128
->type
= FFI_TYPE_STRUCT
;
729 u128
->elements
= (ffi_type
**) (u128
+ 1);
730 u128
->elements
[0] = &ffi_type_uint64
;
731 u128
->elements
[1] = &ffi_type_uint64
;
733 ffi_type_uint128
= u128
;
738 static void psi_ffi_free()
741 free(ffi_type_sint128
);
742 free(ffi_type_uint128
);
746 static struct psi_context_ops ops
= {
756 struct psi_context_ops
*psi_libffi_ops(void)
761 #endif /* HAVE_LIBFFI */