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