14 #undef PACKAGE_BUGREPORT
17 #undef PACKAGE_TARNAME
18 #undef PACKAGE_VERSION
22 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
27 # include <sys/mman.h>
28 # ifndef MAP_ANONYMOUS
29 # define MAP_ANONYMOUS MAP_ANON
34 static void *psi_ffi_closure_alloc(size_t s
, void **code
)
36 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
37 return ffi_closure_alloc(s
, code
);
39 *code
= mmap(NULL
, s
, PROT_EXEC
|PROT_WRITE
|PROT_READ
,
40 MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
41 if (MAP_FAILED
== *code
) {
46 # error "Neither ffi_closure_alloc() nor mmap() available"
50 static ffi_status
psi_ffi_prep_closure(ffi_closure
**closure
, void **code
, ffi_cif
*sig
, void (*handler
)(ffi_cif
*,void*,void**,void*), void *data
) {
51 *closure
= psi_ffi_closure_alloc(sizeof(ffi_closure
), code
);
52 ZEND_ASSERT(*closure
!= NULL
);
54 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
55 return ffi_prep_closure_loc(*closure
, sig
, handler
, data
, *code
);
57 #elif PSI_HAVE_FFI_PREP_CLOSURE
58 return ffi_prep_closure(*code
, sig
, handler
, data
);
60 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
65 static void psi_ffi_closure_free(void *c
)
67 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
70 munmap(c
, sizeof(ffi_closure
));
74 static void psi_ffi_handler(ffi_cif
*_sig
, void *_result
, void **_args
, void *_data
)
76 psi_call(*(zend_execute_data
**)_args
[0], *(zval
**)_args
[1], _data
);
79 static void psi_ffi_callback(ffi_cif
*_sig
, void *_result
, void **_args
, void *_data
)
82 unsigned argc
= _sig
->nargs
;
84 let_callback
*cb
= _data
;
85 decl
*decl_cb
= cb
->decl
;
86 impl_arg
*iarg
= cb
->func
->var
->arg
;
87 zval return_value
, *zargv
= calloc(argc
, sizeof(*zargv
));
88 void *result
, *to_free
= NULL
;
90 ZEND_ASSERT(argc
== cb
->decl
->args
->count
);
92 /* prepare args for the userland call */
93 for (i
= 0; i
< argc
; ++i
) {
94 cb
->decl
->args
->args
[i
]->ptr
= argv
[i
];
96 for (i
= 0; i
< cb
->args
->count
; ++i
) {
97 psi_do_set(&zargv
[i
], cb
->args
->vals
[i
]);
99 zend_fcall_info_argp(&iarg
->val
.zend
.cb
->fci
, cb
->args
->count
, zargv
);
101 /* callback into userland */
102 ZVAL_UNDEF(&return_value
);
103 iarg
->_zv
= &return_value
;
104 zend_fcall_info_call(&iarg
->val
.zend
.cb
->fci
, &iarg
->val
.zend
.cb
->fcc
, iarg
->_zv
, NULL
);
106 /* marshal return value of the userland call */
107 switch (iarg
->type
->type
) {
108 case PSI_T_BOOL
: zend_parse_arg_bool(iarg
->_zv
, &iarg
->val
.zend
.bval
, NULL
, 0); break;
109 case PSI_T_LONG
: zend_parse_arg_long(iarg
->_zv
, &iarg
->val
.zend
.lval
, NULL
, 0, 1); break;
111 case PSI_T_DOUBLE
: zend_parse_arg_double(iarg
->_zv
, &iarg
->val
.dval
, NULL
, 0); break;
112 case PSI_T_STRING
: zend_parse_arg_str(iarg
->_zv
, &iarg
->val
.zend
.str
, 0); break;
114 result
= cb
->func
->handler(_result
, decl_cb
->func
->type
, iarg
, &to_free
);
116 if (result
!= _result
) {
117 *(void **)_result
= result
;
120 zend_fcall_info_args_clear(&iarg
->val
.zend
.cb
->fci
, 0);
121 for (i
= 0; i
< cb
->args
->count
; ++i
) {
122 zval_ptr_dtor(&zargv
[i
]);
127 static inline ffi_type
*psi_ffi_decl_arg_type(decl_arg
*darg
);
129 typedef struct PSI_LibffiContext
{
134 typedef struct PSI_LibffiCall
{
136 ffi_closure
*closure
;
138 void *params
[1]; /* [type1, type2, NULL, arg1, arg2] ... */
141 static inline ffi_abi
psi_ffi_abi(const char *convention
) {
142 return FFI_DEFAULT_ABI
;
145 static inline PSI_LibffiCall
*PSI_LibffiCallAlloc(PSI_Context
*C
, decl
*decl
) {
147 size_t i
, c
= decl
->args
? decl
->args
->count
: 0;
148 PSI_LibffiCall
*call
= calloc(1, sizeof(*call
) + 2 * c
* sizeof(void *));
150 for (i
= 0; i
< c
; ++i
) {
151 call
->params
[i
] = psi_ffi_decl_arg_type(decl
->args
->args
[i
]);
153 call
->params
[c
] = NULL
;
155 decl
->call
.info
= call
;
156 decl
->call
.rval
= &decl
->func
->ptr
;
158 decl
->call
.args
= (void **) &call
->params
[c
+1];
160 rc
= ffi_prep_cif(&call
->signature
, psi_ffi_abi(decl
->abi
->convention
),
161 c
, psi_ffi_decl_arg_type(decl
->func
), (ffi_type
**) call
->params
);
162 ZEND_ASSERT(FFI_OK
== rc
);
167 static inline ffi_status
PSI_LibffiCallInitClosure(PSI_Context
*C
, PSI_LibffiCall
*call
, impl
*impl
) {
168 PSI_LibffiContext
*context
= C
->context
;
170 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &context
->signature
, psi_ffi_handler
, impl
);
173 static inline ffi_status
PSI_LibffiCallInitCallbackClosure(PSI_Context
*C
, PSI_LibffiCall
*call
, let_callback
*cb
) {
174 return psi_ffi_prep_closure(&call
->closure
, &call
->code
, &call
->signature
, psi_ffi_callback
, cb
);
177 static inline void PSI_LibffiCallFree(PSI_LibffiCall
*call
) {
179 psi_ffi_closure_free(call
->closure
);
184 static inline ffi_type
*psi_ffi_token_type(token_t t
) {
190 return &ffi_type_void
;
192 return &ffi_type_sint8
;
194 return &ffi_type_uint8
;
196 return &ffi_type_sint16
;
198 return &ffi_type_uint16
;
200 return &ffi_type_sint32
;
202 return &ffi_type_uint32
;
204 return &ffi_type_sint64
;
206 return &ffi_type_uint64
;
208 return &ffi_type_uchar
;
211 return &ffi_type_sint
;
213 return &ffi_type_slong
;
215 return &ffi_type_float
;
217 return &ffi_type_double
;
218 #ifdef HAVE_LONG_DOUBLE
219 case PSI_T_LONG_DOUBLE
:
220 return &ffi_type_longdouble
;
224 return &ffi_type_pointer
;
227 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
) {
230 return &ffi_type_sint8
;
232 return &ffi_type_sint64
;
234 return &ffi_type_pointer
;
237 return &ffi_type_double
;
238 EMPTY_SWITCH_DEFAULT_CASE();
242 static void psi_ffi_struct_type_dtor(void *type
) {
243 ffi_type
*strct
= type
;
245 if (strct
->elements
) {
248 for (ptr
= strct
->elements
; *ptr
; ++ptr
) {
251 free(strct
->elements
);
256 static size_t psi_ffi_struct_type_pad(ffi_type
**els
, size_t padding
) {
259 for (i
= 0; i
< padding
; ++i
) {
260 ffi_type
*pad
= malloc(sizeof(*pad
));
262 memcpy(pad
, &ffi_type_schar
, sizeof(*pad
));
269 static ffi_type
**psi_ffi_struct_type_elements(decl_struct
*strct
) {
270 size_t i
, argc
= strct
->args
->count
, nels
= 0, offset
= 0, maxalign
= 0;
271 ffi_type
**els
= calloc(argc
+ 1, sizeof(*els
));
273 for (i
= 0; i
< strct
->args
->count
; ++i
) {
274 decl_arg
*darg
= strct
->args
->args
[i
];
275 ffi_type
*type
= malloc(sizeof(*type
));
278 memcpy(type
, psi_ffi_decl_arg_type(darg
), sizeof(*type
));
280 ZEND_ASSERT(type
->size
== darg
->layout
->len
);
282 if (type
->alignment
> maxalign
) {
283 maxalign
= type
->alignment
;
286 if ((padding
= psi_offset_padding(darg
->layout
->pos
- offset
, type
->alignment
))) {
287 if (nels
+ padding
+ 1 > argc
) {
289 els
= realloc(els
, (argc
+ 1) * sizeof(*els
));
292 psi_ffi_struct_type_pad(&els
[nels
], padding
);
296 ZEND_ASSERT(offset
== darg
->layout
->pos
);
298 offset
= (offset
+ darg
->layout
->len
+ type
->alignment
- 1) & ~(type
->alignment
- 1);
302 /* apply struct alignment padding */
303 offset
= (offset
+ maxalign
- 1) & ~(maxalign
- 1);
305 ZEND_ASSERT(offset
<= strct
->size
);
306 if (offset
< strct
->size
) {
307 psi_ffi_struct_type_pad(&els
[nels
], strct
->size
- offset
);
312 static inline ffi_type
*psi_ffi_decl_type(decl_type
*type
) {
313 decl_type
*real
= real_decl_type(type
);
315 switch (real
->type
) {
317 return &ffi_type_pointer
;
320 if (!real
->strct
->engine
.type
) {
321 ffi_type
*strct
= calloc(1, sizeof(ffi_type
));
323 strct
->type
= FFI_TYPE_STRUCT
;
325 strct
->elements
= psi_ffi_struct_type_elements(real
->strct
);
327 real
->strct
->engine
.type
= strct
;
328 real
->strct
->engine
.dtor
= psi_ffi_struct_type_dtor
;
331 return real
->strct
->engine
.type
;
334 return psi_ffi_decl_arg_type(real
->unn
->args
->args
[0]);
337 return psi_ffi_token_type(real
->type
);
340 static inline ffi_type
*psi_ffi_decl_arg_type(decl_arg
*darg
) {
341 if (darg
->var
->pointer_level
) {
342 return &ffi_type_pointer
;
344 return psi_ffi_decl_type(darg
->type
);
349 static inline PSI_LibffiContext
*PSI_LibffiContextInit(PSI_LibffiContext
*L
) {
353 L
= malloc(sizeof(*L
));
355 memset(L
, 0, sizeof(*L
));
357 L
->params
[0] = &ffi_type_pointer
;
358 L
->params
[1] = &ffi_type_pointer
;
359 rc
= ffi_prep_cif(&L
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
, L
->params
);
360 ZEND_ASSERT(rc
== FFI_OK
);
365 static void psi_ffi_init(PSI_Context
*C
)
367 C
->context
= PSI_LibffiContextInit(NULL
);
370 static void psi_ffi_dtor(PSI_Context
*C
)
375 for (i
= 0; i
< C
->decls
->count
; ++i
) {
376 decl
*decl
= C
->decls
->list
[i
];
378 if (decl
->call
.info
) {
379 PSI_LibffiCallFree(decl
->call
.info
);
387 for (i
= 0; i
< C
->impls
->count
; ++i
) {
388 impl
*impl
= C
->impls
->list
[i
];
390 for (j
= 0; j
< impl
->stmts
->let
.count
; ++j
) {
391 let_stmt
*let
= impl
->stmts
->let
.list
[j
];
393 if (let
->val
&& let
->val
->kind
== PSI_LET_CALLBACK
) {
394 let_callback
*cb
= let
->val
->data
.callback
;
396 if (cb
->decl
&& cb
->decl
->call
.info
) {
397 PSI_LibffiCallFree(cb
->decl
->call
.info
);
406 static zend_function_entry
*psi_ffi_compile(PSI_Context
*C
)
409 zend_function_entry
*zfe
;
415 zfe
= calloc(C
->impls
->count
+ 1, sizeof(*zfe
));
416 for (i
= 0; i
< C
->impls
->count
; ++i
) {
417 zend_function_entry
*zf
= &zfe
[j
];
418 PSI_LibffiCall
*call
;
419 impl
*impl
= C
->impls
->list
[i
];
425 call
= PSI_LibffiCallAlloc(C
, impl
->decl
);
426 if (FFI_OK
!= PSI_LibffiCallInitClosure(C
, call
, impl
)) {
427 PSI_LibffiCallFree(call
);
431 zf
->fname
= impl
->func
->name
+ (impl
->func
->name
[0] == '\\');
432 zf
->num_args
= impl
->func
->args
->count
;
433 zf
->handler
= call
->code
;
434 zf
->arg_info
= psi_internal_arginfo(impl
);
437 for (c
= 0; c
< impl
->stmts
->let
.count
; ++c
) {
438 let_stmt
*let
= impl
->stmts
->let
.list
[c
];
440 if (let
->val
->kind
== PSI_LET_CALLBACK
) {
441 let_callback
*cb
= let
->val
->data
.callback
;
443 call
= PSI_LibffiCallAlloc(C
, cb
->decl
);
444 if (FFI_OK
!= PSI_LibffiCallInitCallbackClosure(C
, call
, cb
)) {
445 PSI_LibffiCallFree(call
);
449 cb
->decl
->call
.sym
= call
->code
;
454 for (i
= 0; i
< C
->decls
->count
; ++i
) {
455 decl
*decl
= C
->decls
->list
[i
];
460 if (decl
->call
.info
) {
464 PSI_LibffiCallAlloc(C
, decl
);
470 static void psi_ffi_call(PSI_Context
*C
, decl_callinfo
*decl_call
, impl_vararg
*va
) {
471 PSI_LibffiCall
*call
= decl_call
->info
;
476 size_t i
, nfixedargs
= decl_call
->argc
, ntotalargs
= nfixedargs
+ va
->args
->count
;
477 void **params
= calloc(2 * ntotalargs
+ 2, sizeof(void *));
479 for (i
= 0; i
< nfixedargs
; ++i
) {
480 params
[i
] = call
->params
[i
];
481 params
[i
+ ntotalargs
+ 1] = call
->params
[i
+ nfixedargs
+ 1];
483 for (i
= 0; i
< va
->args
->count
; ++i
) {
484 params
[nfixedargs
+ i
] = psi_ffi_impl_type(va
->types
[i
]);
485 params
[nfixedargs
+ i
+ ntotalargs
+ 1] = &va
->values
[i
];
487 #ifdef PSI_HAVE_FFI_PREP_CIF_VAR
488 rc
= ffi_prep_cif_var(&signature
, call
->signature
.abi
,
489 nfixedargs
, ntotalargs
,
490 call
->signature
.rtype
, (ffi_type
**) params
);
492 /* FIXME: test in config.m4; assume we can just call anyway */
493 rc
= ffi_prep_cif(&signature
, call
->signature
.abi
, ntotalargs
,
494 call
->signature
.rtype
, (ffi_type
**) params
);
496 ZEND_ASSERT(FFI_OK
== rc
);
497 ffi_call(&signature
, FFI_FN(decl_call
->sym
), *decl_call
->rval
, ¶ms
[ntotalargs
+ 1]);
500 ffi_call(&call
->signature
, FFI_FN(decl_call
->sym
), *decl_call
->rval
, decl_call
->args
);
504 static PSI_ContextOps ops
= {
511 PSI_ContextOps
*PSI_Libffi(void)
516 #endif /* HAVE_LIBFFI */