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, last_arg_pos
= -1;
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
)) {
311 if (darg
->layout
->pos
== last_arg_pos
) {
312 /* skip bit fields */
315 last_arg_pos
= darg
->layout
->pos
;
317 type
= malloc(sizeof(*type
));
318 *type
= *psi_ffi_decl_arg_type(darg
);
320 if (type
->alignment
> maxalign
) {
321 maxalign
= type
->alignment
;
324 assert(type
->size
== darg
->layout
->len
);
325 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
326 if (nels
+ padding
+ 1 > argc
) {
328 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
337 psi_ffi_struct_type_pad(&els
[nels
], padding
);
341 assert(offset
== darg
->layout
->pos
);
343 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
347 /* apply struct alignment padding */
348 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
350 assert(offset
<= strct
->size
);
351 if (offset
< strct
->size
) {
352 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
357 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
358 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
360 switch (real
->type
) {
362 if (!real
->real
.strct
->engine
.type
) {
363 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
365 strct
->type
= FFI_TYPE_STRUCT
;
367 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
369 real
->real
.strct
->engine
.type
= strct
;
370 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
373 return real
->real
.strct
->engine
.type
;
377 struct psi_decl_arg
*arg
;
378 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
379 return psi_ffi_decl_arg_type(arg
);
383 return psi_ffi_token_type(real
->type
);
386 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
387 if (darg
->var
->pointer_level
) {
388 return &ffi_type_pointer
;
390 return psi_ffi_decl_type(darg
->type
);
395 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
399 L
= malloc(sizeof(*L
));
401 memset(L
, 0, sizeof(*L
));
403 L
->params
[0] = &ffi_type_pointer
;
404 L
->params
[1] = &ffi_type_pointer
;
405 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
406 assert(rc
== FFI_OK
);
411 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
418 static void psi_ffi_init(struct psi_context
*C
)
420 C
->context
= psi_ffi_context_init(NULL
);
423 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, struct psi_let_exp
*let_exp
) {
424 struct psi_let_callback
*cb
;
425 struct psi_let_func
*fn
= NULL
;
427 switch (let_exp
->kind
) {
428 case PSI_LET_CALLBACK
:
429 cb
= let_exp
->data
.callback
;
431 if (cb
->decl
&& cb
->decl
->info
) {
432 psi_ffi_call_free(cb
->decl
->info
);
438 fn
= let_exp
->data
.func
;
443 struct psi_let_exp
*cb
;
445 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
446 psi_ffi_destroy_callbacks(C
, cb
);
455 static void psi_ffi_dtor(struct psi_context
*C
)
459 struct psi_decl
*decl
;
461 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
463 psi_ffi_call_free(decl
->info
);
470 struct psi_impl
*impl
;
472 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
474 struct psi_let_stmt
*let
;
476 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
477 psi_ffi_destroy_callbacks(C
, let
->exp
);
481 psi_ffi_context_free((void *) &C
->context
);
484 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
,
485 struct psi_ffi_call
*impl_call
, struct psi_let_exp
*let_exp
) {
486 struct psi_ffi_call
*call
;
487 struct psi_let_callback
*cb
;
488 struct psi_let_func
*fn
= NULL
;
490 switch (let_exp
->kind
) {
491 case PSI_LET_CALLBACK
:
492 cb
= let_exp
->data
.callback
;
493 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
494 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, impl_call
, let_exp
)) {
495 psi_ffi_call_free(call
);
499 cb
->decl
->sym
= call
->code
;
505 fn
= let_exp
->data
.func
;
509 struct psi_let_exp
*inner_let
;
511 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
512 psi_ffi_compile_callbacks(C
, impl_call
, inner_let
);
521 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
523 size_t i
= 0, d
= 0, nf
= 0;
524 struct psi_impl
*impl
;
525 struct psi_decl
*decl
;
526 zend_function_entry
*zfe
;
532 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
534 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
536 struct psi_let_stmt
*let
;
537 struct psi_ffi_call
*call
;
538 zend_function_entry
*zf
= &zfe
[nf
];
543 if (!(call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
546 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
547 psi_ffi_call_free(call
);
551 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
552 zf
->handler
= call
->code
;
553 zf
->num_args
= psi_plist_count(impl
->func
->args
);
554 zf
->arg_info
= psi_internal_arginfo(impl
);
557 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
558 psi_ffi_compile_callbacks(C
, call
, let
->exp
);
562 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
567 psi_ffi_call_alloc(C
, decl
);
573 static void psi_ffi_call(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
) {
574 struct psi_ffi_call
*info
= decl
->info
;
575 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
577 info
->impl
.fn
.frame
= frame
;
578 ffi_call(&info
->signature
, FFI_FN(decl
->sym
), rval
, args
);
579 info
->impl
.fn
.frame
= prev
;
582 static void psi_ffi_call_va(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
,
583 size_t va_count
, void **va_types
) {
585 struct psi_ffi_call
*info
= decl
->info
;
586 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
587 size_t argc
= psi_plist_count(decl
->args
);
588 ffi_type
**param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
590 memcpy(param_types
, info
->params
, argc
* sizeof(ffi_type
*));
591 memcpy(param_types
+ argc
, va_types
, va_count
* sizeof(ffi_type
*));
593 psi_ffi_prep_va(&info
->signature
, &signature
, argc
, va_count
, param_types
);
594 info
->impl
.fn
.frame
= frame
;
595 ffi_call(&signature
, FFI_FN(decl
->sym
), rval
, args
);
596 info
->impl
.fn
.frame
= prev
;
600 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
602 case PSI_CONTEXT_QUERY_SELF
:
604 case PSI_CONTEXT_QUERY_TYPE
:
605 return psi_ffi_impl_type(*(token_t
*) arg
);
610 static struct psi_context_ops ops
= {
619 struct psi_context_ops
*psi_libffi_ops(void)
624 #endif /* HAVE_LIBFFI */