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 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 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
**tmp
, **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 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
318 psi_ffi_struct_type_pad(&els
[nels
], padding
);
322 assert(offset
== darg
->layout
->pos
);
324 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
328 /* apply struct alignment padding */
329 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
331 assert(offset
<= strct
->size
);
332 if (offset
< strct
->size
) {
333 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
338 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
339 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
341 switch (real
->type
) {
343 if (!real
->real
.strct
->engine
.type
) {
344 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
346 strct
->type
= FFI_TYPE_STRUCT
;
348 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
350 real
->real
.strct
->engine
.type
= strct
;
351 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
354 return real
->real
.strct
->engine
.type
;
358 struct psi_decl_arg
*arg
;
359 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
360 return psi_ffi_decl_arg_type(arg
);
364 return psi_ffi_token_type(real
->type
);
367 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
368 if (darg
->var
->pointer_level
) {
369 return &ffi_type_pointer
;
371 return psi_ffi_decl_type(darg
->type
);
376 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
380 L
= malloc(sizeof(*L
));
382 memset(L
, 0, sizeof(*L
));
384 L
->params
[0] = &ffi_type_pointer
;
385 L
->params
[1] = &ffi_type_pointer
;
386 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
387 assert(rc
== FFI_OK
);
392 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
399 static void psi_ffi_init(struct psi_context
*C
)
401 C
->context
= psi_ffi_context_init(NULL
);
404 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, struct psi_let_exp
*let_exp
) {
405 struct psi_let_callback
*cb
;
406 struct psi_let_func
*fn
= NULL
;
408 switch (let_exp
->kind
) {
409 case PSI_LET_CALLBACK
:
410 cb
= let_exp
->data
.callback
;
412 if (cb
->decl
&& cb
->decl
->info
) {
413 psi_ffi_call_free(cb
->decl
->info
);
419 fn
= let_exp
->data
.func
;
424 struct psi_let_exp
*cb
;
426 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
427 psi_ffi_destroy_callbacks(C
, cb
);
436 static void psi_ffi_dtor(struct psi_context
*C
)
440 struct psi_decl
*decl
;
442 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
444 psi_ffi_call_free(decl
->info
);
451 struct psi_impl
*impl
;
453 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
455 struct psi_let_stmt
*let
;
457 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
458 psi_ffi_destroy_callbacks(C
, let
->exp
);
462 psi_ffi_context_free((void *) &C
->context
);
465 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
,
466 struct psi_ffi_call
*impl_call
, struct psi_let_exp
*let_exp
) {
467 struct psi_ffi_call
*call
;
468 struct psi_let_callback
*cb
;
469 struct psi_let_func
*fn
= NULL
;
471 switch (let_exp
->kind
) {
472 case PSI_LET_CALLBACK
:
473 cb
= let_exp
->data
.callback
;
474 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
475 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, impl_call
, let_exp
)) {
476 psi_ffi_call_free(call
);
480 cb
->decl
->sym
= call
->code
;
486 fn
= let_exp
->data
.func
;
490 struct psi_let_exp
*inner_let
;
492 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
493 psi_ffi_compile_callbacks(C
, impl_call
, inner_let
);
502 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
504 size_t i
= 0, d
= 0, nf
= 0;
505 struct psi_impl
*impl
;
506 struct psi_decl
*decl
;
507 zend_function_entry
*zfe
;
513 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
515 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
517 struct psi_let_stmt
*let
;
518 struct psi_ffi_call
*call
;
519 zend_function_entry
*zf
= &zfe
[nf
];
524 if (!(call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
527 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
528 psi_ffi_call_free(call
);
532 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
533 zf
->handler
= call
->code
;
534 zf
->num_args
= psi_plist_count(impl
->func
->args
);
535 zf
->arg_info
= psi_internal_arginfo(impl
);
538 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
539 psi_ffi_compile_callbacks(C
, call
, let
->exp
);
543 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
548 psi_ffi_call_alloc(C
, decl
);
554 static void psi_ffi_call(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
) {
555 struct psi_ffi_call
*info
= decl
->info
;
556 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
558 info
->impl
.fn
.frame
= frame
;
559 ffi_call(&info
->signature
, FFI_FN(decl
->sym
), rval
, args
);
560 info
->impl
.fn
.frame
= prev
;
563 static void psi_ffi_call_va(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
,
564 size_t va_count
, void **va_types
) {
566 struct psi_ffi_call
*info
= decl
->info
;
567 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
568 size_t argc
= psi_plist_count(decl
->args
);
569 ffi_type
**param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
571 memcpy(param_types
, info
->params
, argc
* sizeof(ffi_type
*));
572 memcpy(param_types
+ argc
, va_types
, va_count
* sizeof(ffi_type
*));
574 psi_ffi_prep_va(&info
->signature
, &signature
, argc
, va_count
, param_types
);
575 info
->impl
.fn
.frame
= frame
;
576 ffi_call(&signature
, FFI_FN(decl
->sym
), rval
, args
);
577 info
->impl
.fn
.frame
= prev
;
581 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
583 case PSI_CONTEXT_QUERY_SELF
:
585 case PSI_CONTEXT_QUERY_TYPE
:
586 return psi_ffi_impl_type(*(token_t
*) arg
);
591 static struct psi_context_ops ops
= {
600 struct psi_context_ops
*psi_libffi_ops(void)
605 #endif /* HAVE_LIBFFI */