callable and func_ptr typedef fixes
[m6w6/ext-psi] / src / libffi.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include "php.h"
6
7 #ifdef HAVE_LIBFFI
8
9 #include "php_psi.h"
10 #include "libffi.h"
11 #include "engine.h"
12
13 #undef PACKAGE
14 #undef PACKAGE_BUGREPORT
15 #undef PACKAGE_NAME
16 #undef PACKAGE_STRING
17 #undef PACKAGE_TARNAME
18 #undef PACKAGE_VERSION
19
20 #include <ffi.h>
21
22 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
23 # if HAVE_UNISTD_H
24 # include <unistd.h>
25 # endif
26 # if HAVE_SYS_MMAN_H
27 # include <sys/mman.h>
28 # ifndef MAP_ANONYMOUS
29 # define MAP_ANONYMOUS MAP_ANON
30 # endif
31 # endif
32 #endif
33
34 static void *psi_ffi_closure_alloc(size_t s, void **code)
35 {
36 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
37 return ffi_closure_alloc(s, code);
38 #elif HAVE_MMAP
39 *code = mmap(NULL, s, PROT_EXEC|PROT_WRITE|PROT_READ,
40 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
41 if (MAP_FAILED == *code) {
42 return NULL;
43 }
44 return *code;
45 #else
46 return NULL;
47 #endif
48 }
49
50 static void psi_ffi_closure_free(void *c)
51 {
52 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
53 ffi_closure_free(c);
54 #elif HAVE_MMAP
55 munmap(c, sizeof(ffi_closure));
56 #endif
57 }
58
59 static void psi_ffi_handler(ffi_cif *signature, void *_result, void **_args, void *_data);
60 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg);
61
62 static inline ffi_abi psi_ffi_abi(const char *convention) {
63 return FFI_DEFAULT_ABI;
64 }
65 static inline ffi_type *psi_ffi_token_type(token_t t) {
66 switch (t) {
67 default:
68 ZEND_ASSERT(0);
69 /* no break */
70 case PSI_T_VOID:
71 return &ffi_type_void;
72 case PSI_T_INT8:
73 return &ffi_type_sint8;
74 case PSI_T_UINT8:
75 return &ffi_type_uint8;
76 case PSI_T_INT16:
77 return &ffi_type_sint16;
78 case PSI_T_UINT16:
79 return &ffi_type_uint16;
80 case PSI_T_INT32:
81 return &ffi_type_sint32;
82 case PSI_T_UINT32:
83 return &ffi_type_uint32;
84 case PSI_T_INT64:
85 return &ffi_type_sint64;
86 case PSI_T_UINT64:
87 return &ffi_type_uint64;
88 case PSI_T_BOOL:
89 return &ffi_type_uchar;
90 case PSI_T_INT:
91 case PSI_T_ENUM:
92 return &ffi_type_sint;
93 case PSI_T_LONG:
94 return &ffi_type_slong;
95 case PSI_T_FLOAT:
96 return &ffi_type_float;
97 case PSI_T_DOUBLE:
98 return &ffi_type_double;
99 #ifdef HAVE_LONG_DOUBLE
100 case PSI_T_LONG_DOUBLE:
101 return &ffi_type_longdouble;
102 #endif
103 case PSI_T_POINTER:
104 case PSI_T_FUNCTION:
105 return &ffi_type_pointer;
106 }
107 }
108 static inline ffi_type *psi_ffi_impl_type(token_t impl_type) {
109 switch (impl_type) {
110 case PSI_T_BOOL:
111 return &ffi_type_sint8;
112 case PSI_T_INT:
113 return &ffi_type_sint64;
114 case PSI_T_STRING:
115 return &ffi_type_pointer;
116 case PSI_T_FLOAT:
117 case PSI_T_DOUBLE:
118 return &ffi_type_double;
119 EMPTY_SWITCH_DEFAULT_CASE();
120 }
121 return NULL;
122 }
123 static void psi_ffi_struct_type_dtor(void *type) {
124 ffi_type *strct = type;
125
126 if (strct->elements) {
127 ffi_type **ptr;
128
129 for (ptr = strct->elements; *ptr; ++ptr) {
130 free(*ptr);
131 }
132 free(strct->elements);
133 }
134 free(strct);
135 }
136
137 static size_t psi_ffi_struct_type_pad(ffi_type **els, size_t padding) {
138 size_t i;
139
140 for (i = 0; i < padding; ++i) {
141 ffi_type *pad = malloc(sizeof(*pad));
142
143 memcpy(pad, &ffi_type_schar, sizeof(*pad));
144 *els++ = pad;
145 }
146
147 return padding;
148 }
149
150 static ffi_type **psi_ffi_struct_type_elements(decl_struct *strct) {
151 size_t i, argc = strct->args->count, nels = 0, offset = 0, maxalign = 0;
152 ffi_type **els = calloc(argc + 1, sizeof(*els));
153
154 for (i = 0; i < strct->args->count; ++i) {
155 decl_arg *darg = strct->args->args[i];
156 ffi_type *type = malloc(sizeof(*type));
157 size_t padding;
158
159 memcpy(type, psi_ffi_decl_arg_type(darg), sizeof(*type));
160
161 ZEND_ASSERT(type->size == darg->layout->len);
162
163 if (type->alignment > maxalign) {
164 maxalign = type->alignment;
165 }
166
167 if ((padding = psi_offset_padding(darg->layout->pos - offset, type->alignment))) {
168 if (nels + padding + 1 > argc) {
169 argc += padding;
170 els = realloc(els, (argc + 1) * sizeof(*els));
171 els[argc] = NULL;
172 }
173 psi_ffi_struct_type_pad(&els[nels], padding);
174 nels += padding;
175 offset += padding;
176 }
177 ZEND_ASSERT(offset == darg->layout->pos);
178
179 offset = (offset + darg->layout->len + type->alignment - 1) & ~(type->alignment - 1);
180 els[nels++] = type;
181 }
182
183 /* apply struct alignment padding */
184 offset = (offset + maxalign - 1) & ~(maxalign - 1);
185
186 ZEND_ASSERT(offset <= strct->size);
187 if (offset < strct->size) {
188 psi_ffi_struct_type_pad(&els[nels], strct->size - offset);
189 }
190
191 return els;
192 }
193 static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
194 decl_type *real = real_decl_type(type);
195
196 switch (real->type) {
197 case PSI_T_STRUCT:
198 if (!real->strct->engine.type) {
199 ffi_type *strct = calloc(1, sizeof(ffi_type));
200
201 strct->type = FFI_TYPE_STRUCT;
202 strct->size = 0;
203 strct->elements = psi_ffi_struct_type_elements(real->strct);
204
205 real->strct->engine.type = strct;
206 real->strct->engine.dtor = psi_ffi_struct_type_dtor;
207 }
208
209 return real->strct->engine.type;
210
211 case PSI_T_UNION:
212 return psi_ffi_decl_arg_type(real->unn->args->args[0]);
213
214 default:
215 return psi_ffi_token_type(real->type);
216 }
217 }
218 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
219 if (darg->var->pointer_level) {
220 return &ffi_type_pointer;
221 } else {
222 return psi_ffi_decl_type(darg->type);
223 }
224 }
225
226 typedef struct PSI_LibffiContext {
227 ffi_cif signature;
228 ffi_type *params[2];
229 } PSI_LibffiContext;
230
231 typedef struct PSI_LibffiCall {
232 void *code;
233 ffi_closure *closure;
234 ffi_cif signature;
235 void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
236 } PSI_LibffiCall;
237
238 static inline PSI_LibffiCall *PSI_LibffiCallAlloc(PSI_Context *C, decl *decl) {
239 int rc;
240 size_t i, c = decl->args ? decl->args->count : 0;
241 PSI_LibffiCall *call = calloc(1, sizeof(*call) + 2 * c * sizeof(void *));
242
243 for (i = 0; i < c; ++i) {
244 call->params[i] = psi_ffi_decl_arg_type(decl->args->args[i]);
245 }
246 call->params[c] = NULL;
247
248 decl->call.info = call;
249 decl->call.rval = &decl->func->ptr;
250 decl->call.argc = c;
251 decl->call.args = (void **) &call->params[c+1];
252
253 rc = ffi_prep_cif(&call->signature, psi_ffi_abi(decl->abi->convention),
254 c, psi_ffi_decl_arg_type(decl->func), (ffi_type **) call->params);
255 ZEND_ASSERT(FFI_OK == rc);
256
257 return call;
258 }
259
260 static inline void PSI_LibffiCallInitClosure(PSI_Context *C, PSI_LibffiCall *call, impl *impl) {
261 PSI_LibffiContext *context = C->context;
262 int rc;
263
264 call->closure = psi_ffi_closure_alloc(sizeof(ffi_closure), &call->code);
265 ZEND_ASSERT(call->closure != NULL);
266
267 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
268 rc = ffi_prep_closure_loc(
269 call->closure,
270 &context->signature,
271 psi_ffi_handler,
272 impl,
273 call->code);
274
275 #elif PSI_HAVE_FFI_PREP_CLOSURE
276 rc = ffi_prep_closure(call->code, &context->signature, psi_ffi_handler, impl);
277 #else
278 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() available"
279 #endif
280 ZEND_ASSERT(FFI_OK == rc);
281 }
282
283 static inline void PSI_LibffiCallFree(PSI_LibffiCall *call) {
284 if (call->closure) {
285 psi_ffi_closure_free(call->closure);
286 }
287 free(call);
288 }
289
290 static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
291 ffi_status rc;
292
293 if (!L) {
294 L = malloc(sizeof(*L));
295 }
296 memset(L, 0, sizeof(*L));
297
298 L->params[0] = &ffi_type_pointer;
299 L->params[1] = &ffi_type_pointer;
300 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
301 ZEND_ASSERT(rc == FFI_OK);
302
303 return L;
304 }
305
306 static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
307 {
308 psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
309 }
310
311 static void psi_ffi_init(PSI_Context *C)
312 {
313 C->context = PSI_LibffiContextInit(NULL);
314 }
315
316 static void psi_ffi_dtor(PSI_Context *C)
317 {
318 if (C->decls) {
319 size_t i;
320
321 for (i = 0; i < C->decls->count; ++i) {
322 decl *decl = C->decls->list[i];
323
324 if (decl->call.info) {
325 PSI_LibffiCallFree(decl->call.info);
326 }
327 }
328 }
329 free(C->context);
330 }
331
332 static zend_function_entry *psi_ffi_compile(PSI_Context *C)
333 {
334 size_t i, j = 0;
335 zend_function_entry *zfe;
336
337 if (!C->impls) {
338 return NULL;
339 }
340
341 zfe = calloc(C->impls->count + 1, sizeof(*zfe));
342 for (i = 0; i < C->impls->count; ++i) {
343 zend_function_entry *zf = &zfe[j];
344 PSI_LibffiCall *call;
345 impl *impl = C->impls->list[i];
346
347 if (!impl->decl) {
348 continue;
349 }
350
351 call = PSI_LibffiCallAlloc(C, impl->decl);
352 PSI_LibffiCallInitClosure(C, call, impl);
353
354 zf->fname = impl->func->name + (impl->func->name[0] == '\\');
355 zf->num_args = impl->func->args->count;
356 zf->handler = call->code;
357 zf->arg_info = psi_internal_arginfo(impl);
358 ++j;
359 }
360
361 for (i = 0; i < C->decls->count; ++i) {
362 decl *decl = C->decls->list[i];
363
364 if (decl->impl) {
365 continue;
366 }
367
368 PSI_LibffiCallAlloc(C, decl);
369 }
370
371 return zfe;
372 }
373
374 static void psi_ffi_call(PSI_Context *C, decl_callinfo *decl_call, impl_vararg *va) {
375 PSI_LibffiCall *call = decl_call->info;
376
377 if (va) {
378 ffi_status rc;
379 ffi_cif signature;
380 size_t i, nfixedargs = decl_call->argc, ntotalargs = nfixedargs + va->args->count;
381 void **params = calloc(2 * ntotalargs + 2, sizeof(void *));
382
383 for (i = 0; i < nfixedargs; ++i) {
384 params[i] = call->params[i];
385 params[i + ntotalargs + 1] = call->params[i + nfixedargs + 1];
386 }
387 for (i = 0; i < va->args->count; ++i) {
388 params[nfixedargs + i] = psi_ffi_impl_type(va->types[i]);
389 params[nfixedargs + i + ntotalargs + 1] = &va->values[i];
390 }
391 #ifdef PSI_HAVE_FFI_PREP_CIF_VAR
392 rc = ffi_prep_cif_var(&signature, call->signature.abi,
393 nfixedargs, ntotalargs,
394 call->signature.rtype, (ffi_type **) params);
395 #else
396 /* FIXME: test in config.m4; assume we can just call anyway */
397 rc = ffi_prep_cif(&signature, call->signature.abi, ntotalargs,
398 call->signature.rtype, (ffi_type **) params);
399 #endif
400 ZEND_ASSERT(FFI_OK == rc);
401 ffi_call(&signature, FFI_FN(decl_call->sym), *decl_call->rval, &params[ntotalargs + 1]);
402 free(params);
403 } else {
404 ffi_call(&call->signature, FFI_FN(decl_call->sym), *decl_call->rval, decl_call->args);
405 }
406 }
407
408 static PSI_ContextOps ops = {
409 psi_ffi_init,
410 psi_ffi_dtor,
411 psi_ffi_compile,
412 psi_ffi_call,
413 };
414
415 PSI_ContextOps *PSI_Libffi(void)
416 {
417 return &ops;
418 }
419
420 #endif /* HAVE_LIBFFI */