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