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
;
136 return &ffi_type_sint
;
138 return &ffi_type_float
;
140 return &ffi_type_double
;
141 #ifdef HAVE_LONG_DOUBLE
142 case PSI_T_LONG_DOUBLE
:
143 return &ffi_type_longdouble
;
147 return &ffi_type_pointer
;
150 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
153 return &ffi_type_sint8
;
155 return &ffi_type_sint64
;
157 return &ffi_type_pointer
;
160 return &ffi_type_double
;
161 EMPTY_SWITCH_DEFAULT_CASE();
165 static void psi_ffi_struct_type_dtor(void *type
) {
166 ffi_type
*strct
= type
;
168 if (strct
->elements
) {
171 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
174 free(strct
->elements
);
179 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
182 for (i
= 0; i
< padding
; ++i
) {
183 ffi_type
*pad
= malloc(sizeof(*pad
));
185 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
192 static ffi_type
**psi_ffi_struct_type_elements(struct psi_decl_struct
*strct
) {
193 size_t i
= 0, argc
, nels
= 0, offset
= 0, maxalign
= 0, last_arg_pos
= -1;
194 ffi_type
**tmp
, **els
;
195 struct psi_decl_arg
*darg
;
197 argc
= psi_plist_count(strct
->args
);
198 els
= calloc(argc
+ 1, sizeof(*els
));
200 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
204 if (darg
->layout
->pos
== last_arg_pos
) {
205 /* skip bit fields */
208 last_arg_pos
= darg
->layout
->pos
;
210 type
= malloc(sizeof(*type
));
211 *type
= *psi_ffi_decl_arg_type(darg
);
213 if (type
->alignment
> maxalign
) {
214 maxalign
= type
->alignment
;
217 assert(type
->size
<= darg
->layout
->len
);
218 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
219 if (nels
+ padding
+ 1 > argc
) {
221 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
230 psi_ffi_struct_type_pad(&els
[nels
], padding
);
234 assert(offset
== darg
->layout
->pos
);
236 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
240 /* apply struct alignment padding */
241 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
243 assert(offset
<= strct
->size
);
244 if (offset
< strct
->size
) {
245 size_t padding
= strct
->size
- offset
;
247 tmp
= realloc(els
, (padding
+ argc
+ 1) * sizeof(*els
));
254 psi_ffi_struct_type_pad(&els
[nels
], padding
);
255 els
[argc
+ padding
] = NULL
;
260 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
261 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
263 switch (real
->type
) {
265 if (!real
->real
.strct
->engine
.type
) {
266 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
268 strct
->type
= FFI_TYPE_STRUCT
;
270 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
272 real
->real
.strct
->engine
.type
= strct
;
273 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
276 return real
->real
.strct
->engine
.type
;
280 struct psi_decl_arg
*arg
;
281 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
282 return psi_ffi_decl_arg_type(arg
);
286 return psi_ffi_token_type(real
->type
);
289 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
290 if (darg
->var
->pointer_level
) {
291 return &ffi_type_pointer
;
293 return psi_ffi_decl_type(darg
->type
);
297 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
298 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
299 #ifdef HAVE_FFI_STDCALL
300 if (!strcasecmp(convention
, "stdcall")) {
304 #ifdef HAVE_FFI_FASTCALL
305 if (!strcasecmp(convention
, "fastcall")) {
310 return FFI_DEFAULT_ABI
;
313 struct psi_ffi_context
{
318 struct psi_ffi_impl_info
{
319 struct psi_context
*context
;
320 struct psi_call_frame
*frame
;
323 ffi_closure
*closure
;
326 struct psi_ffi_callback_info
{
327 struct psi_ffi_impl_info
*impl_info
;
328 struct psi_let_exp
*let_exp
;
331 ffi_closure
*closure
;
334 struct psi_ffi_decl_info
{
339 static inline struct psi_ffi_decl_info
*psi_ffi_decl_init(struct psi_decl
*decl
) {
342 size_t i
, c
= psi_plist_count(decl
->args
);
343 struct psi_decl_arg
*arg
;
344 struct psi_ffi_decl_info
*info
= calloc(1, sizeof(*info
) + 2 * c
* sizeof(void *));
346 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
347 info
->params
[i
] = psi_ffi_decl_arg_type(arg
);
349 info
->params
[c
] = NULL
;
351 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
352 c
, psi_ffi_decl_arg_type(decl
->func
), info
->params
);
364 static inline void psi_ffi_decl_dtor(struct psi_decl
*decl
) {
371 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
373 struct psi_impl
*impl
= data
;
374 struct psi_ffi_impl_info
*info
= impl
->info
;
376 psi_context_call(info
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], impl
);
379 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
381 struct psi_ffi_callback_info
*cb_info
= data
;
382 struct psi_call_frame_callback cb_data
;
384 assert(cb_info
->impl_info
->frame
);
386 cb_data
.cb
= cb_info
->let_exp
;
387 cb_data
.argc
= sig
->nargs
;
389 cb_data
.rval
= result
;
391 psi_call_frame_do_callback(cb_info
->impl_info
->frame
, &cb_data
);
394 static inline void psi_ffi_callback_init(struct psi_ffi_impl_info
*impl_info
,
395 struct psi_let_exp
*let_exp
) {
396 struct psi_ffi_callback_info
*cb_info
;
397 struct psi_ffi_decl_info
*decl_info
;
398 struct psi_let_callback
*cb
;
399 struct psi_let_func
*fn
= NULL
;
402 switch (let_exp
->kind
) {
403 case PSI_LET_CALLBACK
:
404 cb
= let_exp
->data
.callback
;
405 if (cb
->decl
->info
) {
406 decl_info
= cb
->decl
->info
;
408 decl_info
= psi_ffi_decl_init(cb
->decl
);
411 cb_info
= calloc(1, sizeof(*cb_info
));
412 cb_info
->impl_info
= impl_info
;
413 cb_info
->let_exp
= let_exp
;
414 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
415 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
423 assert(!cb
->decl
->sym
);
424 cb
->decl
->sym
= cb_info
->code
;
430 fn
= let_exp
->data
.func
;
434 struct psi_let_exp
*inner_let
;
436 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
437 psi_ffi_callback_init(impl_info
, inner_let
);
446 static inline void psi_ffi_callback_dtor(struct psi_let_exp
*let_exp
) {
447 struct psi_let_callback
*cb
;
448 struct psi_let_func
*fn
= NULL
;
450 switch (let_exp
->kind
) {
451 case PSI_LET_CALLBACK
:
452 cb
= let_exp
->data
.callback
;
454 psi_ffi_decl_dtor(cb
->decl
);
457 struct psi_ffi_callback_info
*info
= cb
->info
;
460 psi_ffi_closure_free(info
->closure
);
469 fn
= let_exp
->data
.func
;
474 struct psi_let_exp
*cb
;
476 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
477 psi_ffi_callback_dtor(cb
);
486 static inline struct psi_ffi_impl_info
*psi_ffi_impl_init(struct psi_impl
*impl
,
487 struct psi_context
*C
) {
488 struct psi_ffi_context
*context
= C
->context
;
489 struct psi_ffi_impl_info
*info
= calloc(1, sizeof(*info
));
490 struct psi_let_stmt
*let
;
496 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
497 &context
->signature
, psi_ffi_handler
, impl
);
504 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
505 psi_ffi_callback_init(info
, let
->exp
);
508 return impl
->info
= info
;
511 static inline void psi_ffi_impl_dtor(struct psi_impl
*impl
) {
512 struct psi_ffi_impl_info
*info
= impl
->info
;
513 struct psi_let_stmt
*let
;
516 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
517 psi_ffi_callback_dtor(let
->exp
);
522 psi_ffi_closure_free(info
->closure
);
530 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
534 L
= malloc(sizeof(*L
));
536 memset(L
, 0, sizeof(*L
));
538 L
->params
[0] = &ffi_type_pointer
;
539 L
->params
[1] = &ffi_type_pointer
;
540 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
541 assert(rc
== FFI_OK
);
546 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
553 static void psi_ffi_init(struct psi_context
*C
)
555 C
->context
= psi_ffi_context_init(NULL
);
558 static void psi_ffi_dtor(struct psi_context
*C
)
562 struct psi_decl
*decl
;
564 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
565 psi_ffi_decl_dtor(decl
);
571 struct psi_impl
*impl
;
573 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
574 psi_ffi_impl_dtor(impl
);
577 psi_ffi_context_free((void *) &C
->context
);
581 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
583 size_t i
= 0, d
= 0, nf
= 0;
584 struct psi_impl
*impl
;
585 struct psi_decl
*decl
;
586 zend_function_entry
*zfe
;
592 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
594 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
595 zend_function_entry
*zf
= &zfe
[nf
];
600 if (!psi_ffi_decl_init(impl
->decl
)) {
603 if (!psi_ffi_impl_init(impl
, C
)) {
607 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
608 zf
->handler
= ((struct psi_ffi_impl_info
*) impl
->info
)->code
;
609 zf
->num_args
= psi_plist_count(impl
->func
->args
);
610 zf
->arg_info
= psi_internal_arginfo(impl
);
614 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
619 psi_ffi_decl_init(decl
);
625 static inline void psi_ffi_call_ex(struct psi_call_frame
*frame
) {
626 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
627 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
628 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
629 struct psi_ffi_impl_info
*impl_info
;
630 struct psi_call_frame
*prev
;
633 impl_info
= impl
->info
;
634 prev
= impl_info
->frame
;
635 impl_info
->frame
= frame
;
637 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
638 psi_call_frame_get_rpointer(frame
),
639 psi_call_frame_get_arg_pointers(frame
));
641 impl_info
->frame
= prev
;
645 static inline void psi_ffi_call_va(struct psi_call_frame
*frame
) {
647 struct psi_call_frame
*prev
;
648 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
649 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
650 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
651 struct psi_ffi_impl_info
*impl_info
;
652 size_t i
, va_count
, argc
;
653 ffi_type
**param_types
;
655 argc
= psi_plist_count(decl
->args
);
656 va_count
= psi_call_frame_num_var_args(frame
);
657 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
658 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
659 for (i
= 0; i
< va_count
; ++i
) {
660 struct psi_call_frame_argument
*frame_arg
;
662 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
663 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
666 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
669 impl_info
= impl
->info
;
670 prev
= impl_info
->frame
;
671 impl_info
->frame
= frame
;
673 ffi_call(&signature
, FFI_FN(decl
->sym
),
674 psi_call_frame_get_rpointer(frame
),
675 psi_call_frame_get_arg_pointers(frame
));
677 impl_info
->frame
= prev
;
683 static void psi_ffi_call(struct psi_call_frame
*frame
) {
684 if (psi_call_frame_num_var_args(frame
)) {
685 psi_ffi_call_va(frame
);
687 psi_ffi_call_ex(frame
);
691 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
693 case PSI_CONTEXT_QUERY_SELF
:
695 case PSI_CONTEXT_QUERY_TYPE
:
696 return psi_ffi_impl_type(*(token_t
*) arg
);
701 static struct psi_context_ops ops
= {
709 struct psi_context_ops
*psi_libffi_ops(void)
714 #endif /* HAVE_LIBFFI */