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
, nels
= 0, offset
= 0, maxalign
= 0, last_arg_pos
= -1;
304 ffi_type
**tmp
, **els
;
305 struct psi_decl_arg
*darg
;
307 argc
= psi_plist_count(strct
->args
);
308 els
= calloc(argc
+ 1, sizeof(*els
));
310 while (psi_plist_get(strct
->args
, i
++, &darg
)) {
314 if (darg
->layout
->pos
== last_arg_pos
) {
315 /* skip bit fields */
318 last_arg_pos
= darg
->layout
->pos
;
320 type
= malloc(sizeof(*type
));
321 *type
= *psi_ffi_decl_arg_type(darg
);
323 if (type
->alignment
> maxalign
) {
324 maxalign
= type
->alignment
;
327 assert(type
->size
== darg
->layout
->len
);
328 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
329 if (nels
+ padding
+ 1 > argc
) {
331 tmp
= realloc(els
, (argc
+ 1) * sizeof(*els
));
340 psi_ffi_struct_type_pad(&els
[nels
], padding
);
344 assert(offset
== darg
->layout
->pos
);
346 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
350 /* apply struct alignment padding */
351 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
353 assert(offset
<= strct
->size
);
354 if (offset
< strct
->size
) {
355 size_t padding
= strct
->size
- offset
;
357 tmp
= realloc(els
, (padding
+ argc
+ 1) * sizeof(*els
));
364 psi_ffi_struct_type_pad(&els
[nels
], padding
);
365 els
[argc
+ padding
] = NULL
;
370 static inline ffi_type
*psi_ffi_decl_type(struct psi_decl_type
*type
) {
371 struct psi_decl_type
*real
= psi_decl_type_get_real(type
);
373 switch (real
->type
) {
375 if (!real
->real
.strct
->engine
.type
) {
376 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
378 strct
->type
= FFI_TYPE_STRUCT
;
380 strct
->elements
= psi_ffi_struct_type_elements(real
->real
.strct
);
382 real
->real
.strct
->engine
.type
= strct
;
383 real
->real
.strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
386 return real
->real
.strct
->engine
.type
;
390 struct psi_decl_arg
*arg
;
391 psi_plist_get(real
->real
.unn
->args
, 0, &arg
);
392 return psi_ffi_decl_arg_type(arg
);
396 return psi_ffi_token_type(real
->type
);
399 static inline ffi_type
*psi_ffi_decl_arg_type(struct psi_decl_arg
*darg
) {
400 if (darg
->var
->pointer_level
) {
401 return &ffi_type_pointer
;
403 return psi_ffi_decl_type(darg
->type
);
408 static inline struct psi_ffi_context
*psi_ffi_context_init(struct psi_ffi_context
*L
) {
412 L
= malloc(sizeof(*L
));
414 memset(L
, 0, sizeof(*L
));
416 L
->params
[0] = &ffi_type_pointer
;
417 L
->params
[1] = &ffi_type_pointer
;
418 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
419 assert(rc
== FFI_OK
);
424 static inline void psi_ffi_context_free(struct psi_ffi_context
**L
) {
431 static void psi_ffi_init(struct psi_context
*C
)
433 C
->context
= psi_ffi_context_init(NULL
);
436 static inline void psi_ffi_destroy_callbacks(struct psi_context
*C
, struct psi_let_exp
*let_exp
) {
437 struct psi_let_callback
*cb
;
438 struct psi_let_func
*fn
= NULL
;
440 switch (let_exp
->kind
) {
441 case PSI_LET_CALLBACK
:
442 cb
= let_exp
->data
.callback
;
444 if (cb
->decl
&& cb
->decl
->info
) {
445 psi_ffi_call_free(cb
->decl
->info
);
451 fn
= let_exp
->data
.func
;
456 struct psi_let_exp
*cb
;
458 while (psi_plist_get(fn
->inner
, i
++, &cb
)) {
459 psi_ffi_destroy_callbacks(C
, cb
);
468 static void psi_ffi_dtor(struct psi_context
*C
)
472 struct psi_decl
*decl
;
474 while (psi_plist_get(C
->decls
, i
++, &decl
)) {
476 psi_ffi_call_free(decl
->info
);
483 struct psi_impl
*impl
;
485 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
487 struct psi_let_stmt
*let
;
489 while (psi_plist_get(impl
->stmts
.let
, j
++, &let
)) {
490 psi_ffi_destroy_callbacks(C
, let
->exp
);
494 psi_ffi_context_free((void *) &C
->context
);
497 static inline void psi_ffi_compile_callbacks(struct psi_context
*C
,
498 struct psi_ffi_call
*impl_call
, struct psi_let_exp
*let_exp
) {
499 struct psi_ffi_call
*call
;
500 struct psi_let_callback
*cb
;
501 struct psi_let_func
*fn
= NULL
;
503 switch (let_exp
->kind
) {
504 case PSI_LET_CALLBACK
:
505 cb
= let_exp
->data
.callback
;
506 if ((call
= psi_ffi_call_alloc(C
, cb
->decl
))) {
507 if (FFI_OK
!= psi_ffi_call_init_callback_closure(C
, call
, impl_call
, let_exp
)) {
508 psi_ffi_call_free(call
);
512 cb
->decl
->sym
= call
->code
;
518 fn
= let_exp
->data
.func
;
522 struct psi_let_exp
*inner_let
;
524 while (psi_plist_get(fn
->inner
, i
++, &inner_let
)) {
525 psi_ffi_compile_callbacks(C
, impl_call
, inner_let
);
534 static zend_function_entry
*psi_ffi_compile(struct psi_context
*C
)
536 size_t i
= 0, d
= 0, nf
= 0;
537 struct psi_impl
*impl
;
538 struct psi_decl
*decl
;
539 zend_function_entry
*zfe
;
545 zfe
= calloc(psi_plist_count(C
->impls
) + 1, sizeof(*zfe
));
547 while (psi_plist_get(C
->impls
, i
++, &impl
)) {
549 struct psi_let_stmt
*let
;
550 struct psi_ffi_call
*call
;
551 zend_function_entry
*zf
= &zfe
[nf
];
556 if (!(call
= psi_ffi_call_alloc(C
, impl
->decl
))) {
559 if (FFI_OK
!= psi_ffi_call_init_closure(C
, call
, impl
)) {
560 psi_ffi_call_free(call
);
564 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
565 zf
->handler
= call
->code
;
566 zf
->num_args
= psi_plist_count(impl
->func
->args
);
567 zf
->arg_info
= psi_internal_arginfo(impl
);
570 while (psi_plist_get(impl
->stmts
.let
, l
++, &let
)) {
571 psi_ffi_compile_callbacks(C
, call
, let
->exp
);
575 while (psi_plist_get(C
->decls
, d
++, &decl
)) {
580 psi_ffi_call_alloc(C
, decl
);
586 static void psi_ffi_call(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
) {
587 struct psi_ffi_call
*info
= decl
->info
;
588 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
590 info
->impl
.fn
.frame
= frame
;
591 ffi_call(&info
->signature
, FFI_FN(decl
->sym
), rval
, args
);
592 info
->impl
.fn
.frame
= prev
;
595 static void psi_ffi_call_va(struct psi_context
*C
, struct psi_call_frame
*frame
, struct psi_decl
*decl
, void *rval
, void **args
,
596 size_t va_count
, void **va_types
) {
598 struct psi_ffi_call
*info
= decl
->info
;
599 struct psi_call_frame
*prev
= info
->impl
.fn
.frame
;
600 size_t argc
= psi_plist_count(decl
->args
);
601 ffi_type
**param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
603 memcpy(param_types
, info
->params
, argc
* sizeof(ffi_type
*));
604 memcpy(param_types
+ argc
, va_types
, va_count
* sizeof(ffi_type
*));
606 psi_ffi_prep_va(&info
->signature
, &signature
, argc
, va_count
, param_types
);
607 info
->impl
.fn
.frame
= frame
;
608 ffi_call(&signature
, FFI_FN(decl
->sym
), rval
, args
);
609 info
->impl
.fn
.frame
= prev
;
613 static void *psi_ffi_query(struct psi_context
*C
, enum psi_context_query q
, void *arg
) {
615 case PSI_CONTEXT_QUERY_SELF
:
617 case PSI_CONTEXT_QUERY_TYPE
:
618 return psi_ffi_impl_type(*(token_t
*) arg
);
623 static struct psi_context_ops ops
= {
632 struct psi_context_ops
*psi_libffi_ops(void)
637 #endif /* HAVE_LIBFFI */