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 ZEND_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 ZEND_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 return FFI_DEFAULT_ABI
;
162 static inline struct psi_ffi_call
*psi_ffi_call_alloc(struct psi_context
*C
, struct psi_decl
*decl
) {
164 size_t i
, c
= psi_plist_count(decl
->args
);
165 struct psi_ffi_call
*call
= calloc(1, sizeof(*call
) + 2 * c
* sizeof(void *));
166 struct psi_decl_arg
*arg
;
171 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
172 call
->params
[i
] = psi_ffi_decl_arg_type(arg
);
174 call
->params
[c
] = NULL
;
176 rc
= ffi_prep_cif(&call
->signature
, psi_ffi_abi(decl
->abi
->convention
),
177 c
, psi_ffi_decl_arg_type(decl
->func
), call
->params
);
178 ZEND_ASSERT(FFI_OK
== rc
);
183 static inline ffi_status
psi_ffi_call_init_closure(struct psi_context
*C
, struct psi_ffi_call
*call
, struct psi_impl
*impl
) {
184 struct psi_ffi_context
*context
= C
->context
;
186 call
->impl
.fn
.impl
= impl
;
187 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &context
->signature
, psi_ffi_handler
, call
);
190 static inline ffi_status
psi_ffi_call_init_callback_closure(struct psi_context
*C
,
191 struct psi_ffi_call
*call
, struct psi_ffi_call
*impl_call
,
192 struct psi_let_exp
*cb
) {
193 call
->impl
.cb
.let_exp
= cb
;
194 call
->impl
.cb
.impl_call
= impl_call
;
195 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &call
->signature
, psi_ffi_callback
, call
);
198 static inline void psi_ffi_call_free(struct psi_ffi_call
*call
) {
200 psi_ffi_closure_free(call
->closure
);
205 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
211 return &ffi_type_void
;
213 return &ffi_type_sint8
;
215 return &ffi_type_uint8
;
217 return &ffi_type_sint16
;
219 return &ffi_type_uint16
;
221 return &ffi_type_sint32
;
223 return &ffi_type_uint32
;
225 return &ffi_type_sint64
;
227 return &ffi_type_uint64
;
229 return &ffi_type_uchar
;
232 return &ffi_type_sint
;
234 return &ffi_type_slong
;
236 return &ffi_type_float
;
238 return &ffi_type_double
;
239 #ifdef HAVE_LONG_DOUBLE
240 case PSI_T_LONG_DOUBLE
:
241 return &ffi_type_longdouble
;
245 return &ffi_type_pointer
;
248 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
251 return &ffi_type_sint8
;
253 return &ffi_type_sint64
;
255 return &ffi_type_pointer
;
258 return &ffi_type_double
;
259 EMPTY_SWITCH_DEFAULT_CASE();
263 static void psi_ffi_struct_type_dtor(void *type
) {
264 ffi_type
*strct
= type
;
266 if (strct
->elements
) {
269 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
272 free(strct
->elements
);
277 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
280 for (i
= 0; i
< padding
; ++i
) {
281 ffi_type
*pad
= malloc(sizeof(*pad
));
283 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
290 static ffi_type
**psi_ffi_struct_type_elements(struct psi_decl_struct
*strct
) {
291 size_t i
= 0, argc
= psi_plist_count(strct
->args
), nels
= 0, offset
= 0, maxalign
= 0;
292 ffi_type
**els
= calloc(argc
+ 1, sizeof(*els
));
293 struct psi_decl_arg
*darg
;
295 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
296 ffi_type
*type
= malloc(sizeof(*type
));
299 *type
= *psi_ffi_decl_arg_type(darg
);
301 if (type
->alignment
> maxalign
) {
302 maxalign
= type
->alignment
;
305 assert(type
->size
== darg
->layout
->len
);
306 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
307 if (nels
+ padding
+ 1 > argc
) {
309 els
= realloc(els
, (argc
+ 1) * sizeof(*els
));
312 psi_ffi_struct_type_pad(&els
[nels
], padding
);
316 assert(offset
== darg
->layout
->pos
);
318 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
322 /* apply struct alignment padding */
323 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
325 assert(offset
<= strct
->size
);
326 if (offset
< strct
->size
) {
327 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
332 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
333 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
335 switch (real
->type
) {
337 if (!real
->real
.strct
->engine
.type
) {
338 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
340 strct
->type
= FFI_TYPE_STRUCT
;
342 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
344 real
->real
.strct
->engine
.type
= strct
;
345 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
348 return real
->real
.strct
->engine
.type
;
352 struct psi_decl_arg
*arg
;
353 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
354 return psi_ffi_decl_arg_type(arg
);
358 return psi_ffi_token_type(real
->type
);
361 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
362 if (darg
->var
->pointer_level
) {
363 return &ffi_type_pointer
;
365 return psi_ffi_decl_type(darg
->type
);
370 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
374 L
= malloc(sizeof(*L
));
376 memset(L
, 0, sizeof(*L
));
378 L
->params
[0] = &ffi_type_pointer
;
379 L
->params
[1] = &ffi_type_pointer
;
380 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
381 ZEND_ASSERT(rc
== FFI_OK
);
386 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
393 static void psi_ffi_init(struct psi_context
*C
)
395 C
->context
= psi_ffi_context_init(NULL
);
398 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, struct psi_let_exp
*let_exp
) {
399 struct psi_let_callback
*cb
;
400 struct psi_let_func
*fn
= NULL
;
402 switch (let_exp
->kind
) {
403 case PSI_LET_CALLBACK
:
404 cb
= let_exp
->data
.callback
;
406 if (cb
->decl
&& cb
->decl
->info
) {
407 psi_ffi_call_free(cb
->decl
->info
);
413 fn
= let_exp
->data
.func
;
418 struct psi_let_exp
*cb
;
420 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
421 psi_ffi_destroy_callbacks(C
, cb
);
430 static void psi_ffi_dtor(struct psi_context
*C
)
434 struct psi_decl
*decl
;
436 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
438 psi_ffi_call_free(decl
->info
);
445 struct psi_impl
*impl
;
447 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
449 struct psi_let_stmt
*let
;
451 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
452 psi_ffi_destroy_callbacks(C
, let
->exp
);
456 psi_ffi_context_free((void *) &C
->context
);
459 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
,
460 struct psi_ffi_call
*impl_call
, struct psi_let_exp
*let_exp
) {
461 struct psi_ffi_call
*call
;
462 struct psi_let_callback
*cb
;
463 struct psi_let_func
*fn
= NULL
;
465 switch (let_exp
->kind
) {
466 case PSI_LET_CALLBACK
:
467 cb
= let_exp
->data
.callback
;
468 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
469 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, impl_call
, let_exp
)) {
470 psi_ffi_call_free(call
);
474 cb
->decl
->sym
= call
->code
;
480 fn
= let_exp
->data
.func
;
484 struct psi_let_exp
*inner_let
;
486 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
487 psi_ffi_compile_callbacks(C
, impl_call
, inner_let
);
496 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
498 size_t i
= 0, d
= 0, nf
= 0;
499 struct psi_impl
*impl
;
500 struct psi_decl
*decl
;
501 zend_function_entry
*zfe
;
507 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
509 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
511 struct psi_let_stmt
*let
;
512 struct psi_ffi_call
*call
;
513 zend_function_entry
*zf
= &zfe
[nf
];
518 if (!(call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
521 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
522 psi_ffi_call_free(call
);
526 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
527 zf
->handler
= call
->code
;
528 zf
->num_args
= psi_plist_count(impl
->func
->args
);
529 zf
->arg_info
= psi_internal_arginfo(impl
);
532 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
533 psi_ffi_compile_callbacks(C
, call
, let
->exp
);
537 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
542 psi_ffi_call_alloc(C
, decl
);
548 static void psi_ffi_call(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
) {
549 struct psi_ffi_call
*info
= decl
->info
;
550 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
552 info
->impl
.fn
.frame
= frame
;
553 ffi_call(&info
->signature
, FFI_FN(decl
->sym
), rval
, args
);
554 info
->impl
.fn
.frame
= prev
;
557 static void psi_ffi_call_va(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
,
558 size_t va_count
, void **va_types
) {
560 struct psi_ffi_call
*info
= decl
->info
;
561 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
562 size_t argc
= psi_plist_count(decl
->args
);
563 ffi_type
**param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
565 memcpy(param_types
, info
->params
, argc
* sizeof(ffi_type
*));
566 memcpy(param_types
+ argc
, va_types
, va_count
* sizeof(ffi_type
*));
568 psi_ffi_prep_va(&info
->signature
, &signature
, argc
, va_count
, param_types
);
569 info
->impl
.fn
.frame
= frame
;
570 ffi_call(&signature
, FFI_FN(decl
->sym
), rval
, args
);
571 info
->impl
.fn
.frame
= prev
;
575 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
577 case PSI_CONTEXT_QUERY_SELF
:
579 case PSI_CONTEXT_QUERY_TYPE
:
580 return psi_ffi_impl_type(*(token_t
*) arg
);
585 static struct psi_context_ops ops
= {
594 struct psi_context_ops
*psi_libffi_ops(void)
599 #endif /* HAVE_LIBFFI */