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 struct psi_ffi_context
{
115 struct psi_ffi_call
{
116 struct psi_context
*context
;
119 struct psi_impl
*impl
;
120 struct psi_call_frame
*frame
;
123 struct psi_let_exp
*let_exp
;
124 struct psi_ffi_call
*impl_call
;
128 ffi_closure
*closure
;
130 ffi_type
*params
[1]; /* [type1, type2, ... ] */
133 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
135 struct psi_ffi_call
*call
= data
;
137 psi_context_call(call
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], call
->impl
.fn
.impl
);
140 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
142 struct psi_ffi_call
*call
= data
, *impl_call
= call
->impl
.cb
.impl_call
;
144 if (impl_call
->impl
.fn
.frame
) {
145 struct psi_call_frame_callback cbdata
;
147 cbdata
.cb
= call
->impl
.cb
.let_exp
;
148 cbdata
.argc
= sig
->nargs
;
150 cbdata
.rval
= result
;
152 psi_call_frame_do_callback(impl_call
->impl
.fn
.frame
, &cbdata
);
158 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
159 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
160 #ifdef HAVE_FFI_STDCALL
161 if (!strcasecmp(convention
, "stdcall")) {
165 #ifdef HAVE_FFI_FASTCALL
166 if (!strcasecmp(convention
, "fastcall")) {
171 return FFI_DEFAULT_ABI
;
174 static inline struct psi_ffi_call
*psi_ffi_call_alloc(struct psi_context
*C
, struct psi_decl
*decl
) {
176 size_t i
, c
= psi_plist_count(decl
->args
);
177 struct psi_ffi_call
*call
= calloc(1, sizeof(*call
) + 2 * c
* sizeof(void *));
178 struct psi_decl_arg
*arg
;
183 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
184 call
->params
[i
] = psi_ffi_decl_arg_type(arg
);
186 call
->params
[c
] = NULL
;
188 rc
= ffi_prep_cif(&call
->signature
, psi_ffi_abi(decl
->abi
->convention
),
189 c
, psi_ffi_decl_arg_type(decl
->func
), call
->params
);
190 assert(FFI_OK
== rc
);
195 static inline ffi_status
psi_ffi_call_init_closure(struct psi_context
*C
, struct psi_ffi_call
*call
, struct psi_impl
*impl
) {
196 struct psi_ffi_context
*context
= C
->context
;
198 call
->impl
.fn
.impl
= impl
;
199 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &context
->signature
, psi_ffi_handler
, call
);
202 static inline ffi_status
psi_ffi_call_init_callback_closure(struct psi_context
*C
,
203 struct psi_ffi_call
*call
, struct psi_ffi_call
*impl_call
,
204 struct psi_let_exp
*cb
) {
205 call
->impl
.cb
.let_exp
= cb
;
206 call
->impl
.cb
.impl_call
= impl_call
;
207 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &call
->signature
, psi_ffi_callback
, call
);
210 static inline void psi_ffi_call_free(struct psi_ffi_call
*call
) {
212 psi_ffi_closure_free(call
->closure
);
217 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
223 return &ffi_type_void
;
225 return &ffi_type_sint8
;
227 return &ffi_type_uint8
;
229 return &ffi_type_sint16
;
231 return &ffi_type_uint16
;
233 return &ffi_type_sint32
;
235 return &ffi_type_uint32
;
237 return &ffi_type_sint64
;
239 return &ffi_type_uint64
;
241 return &ffi_type_uchar
;
244 return &ffi_type_sint
;
246 return &ffi_type_slong
;
248 return &ffi_type_float
;
250 return &ffi_type_double
;
251 #ifdef HAVE_LONG_DOUBLE
252 case PSI_T_LONG_DOUBLE
:
253 return &ffi_type_longdouble
;
257 return &ffi_type_pointer
;
260 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
263 return &ffi_type_sint8
;
265 return &ffi_type_sint64
;
267 return &ffi_type_pointer
;
270 return &ffi_type_double
;
271 EMPTY_SWITCH_DEFAULT_CASE();
275 static void psi_ffi_struct_type_dtor(void *type
) {
276 ffi_type
*strct
= type
;
278 if (strct
->elements
) {
281 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
284 free(strct
->elements
);
289 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
292 for (i
= 0; i
< padding
; ++i
) {
293 ffi_type
*pad
= malloc(sizeof(*pad
));
295 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
302 static ffi_type
**psi_ffi_struct_type_elements(struct psi_decl_struct
*strct
) {
303 size_t i
= 0, argc
= psi_plist_count(strct
->args
), nels
= 0, offset
= 0, maxalign
= 0;
304 ffi_type
**tmp
, **els
= calloc(argc
+ 1, sizeof(*els
));
305 struct psi_decl_arg
*darg
;
307 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
308 ffi_type
*type
= malloc(sizeof(*type
));
311 *type
= *psi_ffi_decl_arg_type(darg
);
313 if (type
->alignment
> maxalign
) {
314 maxalign
= type
->alignment
;
317 assert(type
->size
== darg
->layout
->len
);
318 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
319 if (nels
+ padding
+ 1 > argc
) {
321 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
330 psi_ffi_struct_type_pad(&els
[nels
], padding
);
334 assert(offset
== darg
->layout
->pos
);
336 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
340 /* apply struct alignment padding */
341 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
343 assert(offset
<= strct
->size
);
344 if (offset
< strct
->size
) {
345 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
350 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
351 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
353 switch (real
->type
) {
355 if (!real
->real
.strct
->engine
.type
) {
356 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
358 strct
->type
= FFI_TYPE_STRUCT
;
360 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
362 real
->real
.strct
->engine
.type
= strct
;
363 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
366 return real
->real
.strct
->engine
.type
;
370 struct psi_decl_arg
*arg
;
371 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
372 return psi_ffi_decl_arg_type(arg
);
376 return psi_ffi_token_type(real
->type
);
379 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
380 if (darg
->var
->pointer_level
) {
381 return &ffi_type_pointer
;
383 return psi_ffi_decl_type(darg
->type
);
388 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
392 L
= malloc(sizeof(*L
));
394 memset(L
, 0, sizeof(*L
));
396 L
->params
[0] = &ffi_type_pointer
;
397 L
->params
[1] = &ffi_type_pointer
;
398 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
399 assert(rc
== FFI_OK
);
404 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
411 static void psi_ffi_init(struct psi_context
*C
)
413 C
->context
= psi_ffi_context_init(NULL
);
416 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, struct psi_let_exp
*let_exp
) {
417 struct psi_let_callback
*cb
;
418 struct psi_let_func
*fn
= NULL
;
420 switch (let_exp
->kind
) {
421 case PSI_LET_CALLBACK
:
422 cb
= let_exp
->data
.callback
;
424 if (cb
->decl
&& cb
->decl
->info
) {
425 psi_ffi_call_free(cb
->decl
->info
);
431 fn
= let_exp
->data
.func
;
436 struct psi_let_exp
*cb
;
438 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
439 psi_ffi_destroy_callbacks(C
, cb
);
448 static void psi_ffi_dtor(struct psi_context
*C
)
452 struct psi_decl
*decl
;
454 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
456 psi_ffi_call_free(decl
->info
);
463 struct psi_impl
*impl
;
465 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
467 struct psi_let_stmt
*let
;
469 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
470 psi_ffi_destroy_callbacks(C
, let
->exp
);
474 psi_ffi_context_free((void *) &C
->context
);
477 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
,
478 struct psi_ffi_call
*impl_call
, struct psi_let_exp
*let_exp
) {
479 struct psi_ffi_call
*call
;
480 struct psi_let_callback
*cb
;
481 struct psi_let_func
*fn
= NULL
;
483 switch (let_exp
->kind
) {
484 case PSI_LET_CALLBACK
:
485 cb
= let_exp
->data
.callback
;
486 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
487 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, impl_call
, let_exp
)) {
488 psi_ffi_call_free(call
);
492 cb
->decl
->sym
= call
->code
;
498 fn
= let_exp
->data
.func
;
502 struct psi_let_exp
*inner_let
;
504 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
505 psi_ffi_compile_callbacks(C
, impl_call
, inner_let
);
514 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
516 size_t i
= 0, d
= 0, nf
= 0;
517 struct psi_impl
*impl
;
518 struct psi_decl
*decl
;
519 zend_function_entry
*zfe
;
525 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
527 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
529 struct psi_let_stmt
*let
;
530 struct psi_ffi_call
*call
;
531 zend_function_entry
*zf
= &zfe
[nf
];
536 if (!(call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
539 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
540 psi_ffi_call_free(call
);
544 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
545 zf
->handler
= call
->code
;
546 zf
->num_args
= psi_plist_count(impl
->func
->args
);
547 zf
->arg_info
= psi_internal_arginfo(impl
);
550 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
551 psi_ffi_compile_callbacks(C
, call
, let
->exp
);
555 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
560 psi_ffi_call_alloc(C
, decl
);
566 static void psi_ffi_call(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
) {
567 struct psi_ffi_call
*info
= decl
->info
;
568 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
570 info
->impl
.fn
.frame
= frame
;
571 ffi_call(&info
->signature
, FFI_FN(decl
->sym
), rval
, args
);
572 info
->impl
.fn
.frame
= prev
;
575 static void psi_ffi_call_va(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
,
576 size_t va_count
, void **va_types
) {
578 struct psi_ffi_call
*info
= decl
->info
;
579 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
580 size_t argc
= psi_plist_count(decl
->args
);
581 ffi_type
**param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
583 memcpy(param_types
, info
->params
, argc
* sizeof(ffi_type
*));
584 memcpy(param_types
+ argc
, va_types
, va_count
* sizeof(ffi_type
*));
586 psi_ffi_prep_va(&info
->signature
, &signature
, argc
, va_count
, param_types
);
587 info
->impl
.fn
.frame
= frame
;
588 ffi_call(&signature
, FFI_FN(decl
->sym
), rval
, args
);
589 info
->impl
.fn
.frame
= prev
;
593 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
595 case PSI_CONTEXT_QUERY_SELF
:
597 case PSI_CONTEXT_QUERY_TYPE
:
598 return psi_ffi_impl_type(*(token_t
*) arg
);
603 static struct psi_context_ops ops
= {
612 struct psi_context_ops
*psi_libffi_ops(void)
617 #endif /* HAVE_LIBFFI */