flush
[m6w6/ext-psi] / src / libffi.c
1 #include "php.h"
2 #include "php_psi.h"
3 #include "libffi.h"
4
5 #undef PACKAGE
6 #undef PACKAGE_BUGREPORT
7 #undef PACKAGE_NAME
8 #undef PACKAGE_STRING
9 #undef PACKAGE_TARNAME
10 #undef PACKAGE_VERSION
11
12 #include <ffi.h>
13
14 #ifndef PSI_HAVE_FFI_CLOSURE_ALLOC
15 # if HAVE_UNISTD_H
16 # include <unistd.h>
17 # endif
18 # if HAVE_SYS_MMAN_H
19 # include <sys/mman.h>
20 # ifndef MAP_ANONYMOUS
21 # define MAP_ANONYMOUS MAP_ANON
22 # endif
23 # endif
24 #endif
25
26 static void *psi_ffi_closure_alloc(size_t s, void **code)
27 {
28 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
29 return ffi_closure_alloc(s, code);
30 #elif HAVE_MMAP
31 *code = mmap(NULL, s, PROT_EXEC|PROT_WRITE|PROT_READ,
32 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
33 if (MAP_FAILED == *code) {
34 return NULL;
35 }
36 return *code;
37 #else
38 return NULL;
39 #endif
40 }
41
42 static void psi_ffi_closure_free(void *c)
43 {
44 #ifdef PSI_HAVE_FFI_CLOSURE_ALLOC
45 ffi_closure_free(c);
46 #elif HAVE_MMAP
47 munmap(c, sizeof(ffi_closure));
48 #endif
49 }
50
51 static void handler(ffi_cif *signature, void *_result, void **_args, void *_data);
52
53 static inline ffi_abi psi_ffi_abi(const char *convention) {
54 return FFI_DEFAULT_ABI;
55 }
56 static inline ffi_type *psi_ffi_type(token_t t) {
57 switch (t) {
58 default:
59 ZEND_ASSERT(0);
60 /* no break */
61 case PSI_T_VOID:
62 return &ffi_type_void;
63 case PSI_T_SINT8:
64 return &ffi_type_sint8;
65 case PSI_T_UINT8:
66 return &ffi_type_uint8;
67 case PSI_T_SINT16:
68 return &ffi_type_sint16;
69 case PSI_T_UINT16:
70 return &ffi_type_uint16;
71 case PSI_T_SINT32:
72 return &ffi_type_sint32;
73 case PSI_T_UINT32:
74 return &ffi_type_uint32;
75 case PSI_T_SINT64:
76 return &ffi_type_sint64;
77 case PSI_T_UINT64:
78 return &ffi_type_uint64;
79 case PSI_T_BOOL:
80 return &ffi_type_uchar;
81 case PSI_T_CHAR:
82 return &ffi_type_schar;
83 case PSI_T_SHORT:
84 return &ffi_type_sshort;
85 case PSI_T_INT:
86 return &ffi_type_sint;
87 case PSI_T_LONG:
88 return &ffi_type_slong;
89 case PSI_T_FLOAT:
90 return &ffi_type_float;
91 case PSI_T_DOUBLE:
92 return &ffi_type_double;
93 }
94 }
95 static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
96 return psi_ffi_type(real_decl_type(type)->type);
97 }
98 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
99 if (darg->var->pointer_level) {
100 return &ffi_type_pointer;
101 } else {
102 return psi_ffi_decl_type(darg->type);
103 }
104 }
105
106 typedef struct PSI_LibffiContext {
107 ffi_cif signature;
108 ffi_type *params[2];
109 struct {
110 struct PSI_LibffiData **list;
111 size_t count;
112 } data;
113 } PSI_LibffiContext;
114
115 typedef struct PSI_LibffiData {
116 PSI_LibffiContext *context;
117 impl *impl;
118 zend_internal_arg_info *arginfo;
119 void *code;
120 ffi_closure *closure;
121 ffi_cif signature;
122 ffi_type *params[1];
123 } PSI_LibffiData;
124
125 static inline PSI_LibffiData *PSI_LibffiDataAlloc(PSI_LibffiContext *context, impl *impl) {
126 ffi_status rc;
127 size_t i, c = impl->decl->args ? impl->decl->args->count : 0;
128 PSI_LibffiData *data = malloc(sizeof(*data) + c * sizeof(ffi_type *));
129
130 data->context = context;
131 data->impl = impl;
132 data->arginfo = psi_internal_arginfo(impl);
133 for (i = 0; i < c; ++i) {
134 data->params[i] = psi_ffi_decl_arg_type(impl->decl->args->args[i]);
135 }
136 data->params[c] = NULL;
137
138 rc = ffi_prep_cif(
139 &data->signature,
140 psi_ffi_abi(data->impl->decl->abi->convention),
141 c,
142 psi_ffi_decl_arg_type(data->impl->decl->func),
143 data->params);
144 ZEND_ASSERT(FFI_OK == rc);
145
146 data->closure = psi_ffi_closure_alloc(sizeof(ffi_closure), &data->code);
147 ZEND_ASSERT(data->closure != NULL);
148 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
149 rc = ffi_prep_closure_loc(
150 data->closure,
151 &context->signature,
152 handler,
153 data,
154 data->code);
155 ZEND_ASSERT(FFI_OK == rc);
156 #elif PSI_HAVE_FFI_PREP_CLOSURE
157 rc = ffi_prep_closure(data->code, &context->signature, handler, data);
158 ZEND_ASSERT(FFI_OK == rc);
159 #else
160 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() available"
161 #endif
162
163 context->data.list = realloc(context->data.list, ++context->data.count * sizeof(*context->data.list));
164 context->data.list[context->data.count-1] = data;
165
166 return data;
167 }
168
169 static inline void PSI_LibffiDataFree(PSI_LibffiData *data) {
170 psi_ffi_closure_free(data->closure);
171 free(data->arginfo);
172 free(data);
173 }
174
175 static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
176 ffi_status rc;
177
178 if (!L) {
179 L = malloc(sizeof(*L));
180 }
181 memset(L, 0, sizeof(*L));
182
183 L->params[0] = &ffi_type_pointer;
184 L->params[1] = &ffi_type_pointer;
185 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
186 ZEND_ASSERT(rc == FFI_OK);
187
188 return L;
189 }
190
191 static inline void PSI_LibffiContextDtor(PSI_LibffiContext *L) {
192 size_t i;
193
194 for (i = 0; i < L->data.count; ++i) {
195 PSI_LibffiDataFree(L->data.list[i]);
196 }
197 if (L->data.list) {
198 free(L->data.list);
199 }
200 }
201
202 static inline void PSI_LibffiContextFree(PSI_LibffiContext **L) {
203 if (*L) {
204 PSI_LibffiContextDtor(*L);
205 free(*L);
206 *L = NULL;
207 }
208 }
209
210 static void handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
211 {
212 PSI_LibffiData *data = _data;
213 size_t i;
214 void **arg_ptr = NULL, **arg_prm = NULL;
215 impl_val ret_val;
216
217 if (SUCCESS != psi_parse_args(*(zend_execute_data **)_args[0], data->impl)) {
218 return;
219 }
220
221 if (data->impl->decl->args) {
222 arg_ptr = malloc(data->impl->decl->args->count * sizeof(*arg_ptr));
223 arg_prm = malloc(data->impl->decl->args->count * sizeof(*arg_prm));
224
225 for (i = 0; i < data->impl->decl->args->count; ++i) {
226 decl_arg *darg = data->impl->decl->args->args[i];
227
228 arg_ptr[i] = psi_do_let(darg);
229 arg_prm[i] = (darg->let->val && darg->let->val->is_reference)
230 ? &arg_ptr[i] : arg_ptr[i];
231
232 darg->let->ptr = arg_ptr[i];
233 }
234 }
235
236 ffi_call(&data->signature, FFI_FN(data->impl->decl->dlptr), &ret_val, arg_prm);
237
238 psi_do_return(data->impl, &ret_val, *(zval **)_args[1]);
239
240 for (i = 0; i < data->impl->stmts->set.count; ++i) {
241 set_stmt *set = data->impl->stmts->set.list[i];
242
243 if (set->arg->_zv) {
244 psi_do_set(set->arg->_zv, set->val->func, set->val->vars);
245 }
246 }
247
248 for (i = 0; i < data->impl->stmts->fre.count; ++i) {
249 free_stmt *fre = data->impl->stmts->fre.list[i];
250
251 psi_do_free(fre);
252 }
253
254 psi_do_clean(data->impl);
255
256 if (arg_ptr) {
257 free(arg_ptr);
258 }
259 if (arg_prm) {
260 free(arg_prm);
261 }
262 }
263
264 static void init(PSI_Context *C)
265 {
266 C->context = PSI_LibffiContextInit(NULL);
267 }
268
269 static void dtor(PSI_Context *C)
270 {
271 PSI_LibffiContextFree((void *) &C->context);
272 }
273
274 static zend_function_entry *compile(PSI_Context *C, PSI_Data *D)
275 {
276 size_t i, j = 0;
277 zend_function_entry *zfe = calloc(D->impls->count + 1, sizeof(*zfe));
278 PSI_LibffiContext *ctx = C->context;
279
280 for (i = 0; i < D->impls->count; ++i) {
281 zend_function_entry *zf = &zfe[j];
282 PSI_LibffiData *data;
283
284 if (!D->impls->list[i]->decl) {
285 continue;
286 }
287
288 data = PSI_LibffiDataAlloc(ctx, D->impls->list[i]);
289 zf->fname = D->impls->list[i]->func->name + (D->impls->list[i]->func->name[0] == '\\');
290 zf->num_args = D->impls->list[i]->func->args->count;
291 zf->handler = data->code;
292 zf->arg_info = data->arginfo;
293 ++j;
294 }
295
296 return zfe;
297 }
298
299 static PSI_ContextOps ops = {
300 init,
301 dtor,
302 compile,
303 };
304
305 PSI_ContextOps *PSI_Libffi(void)
306 {
307 return &ops;
308 }