25456d25ce78d412ad63b2423614e80f1d86a6aa
[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 psi_ffi_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_INT8:
64 return &ffi_type_sint8;
65 case PSI_T_UINT8:
66 return &ffi_type_uint8;
67 case PSI_T_INT16:
68 return &ffi_type_sint16;
69 case PSI_T_UINT16:
70 return &ffi_type_uint16;
71 case PSI_T_INT32:
72 return &ffi_type_sint32;
73 case PSI_T_UINT32:
74 return &ffi_type_uint32;
75 case PSI_T_INT64:
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_INT:
82 return &ffi_type_sint;
83 case PSI_T_FLOAT:
84 return &ffi_type_float;
85 case PSI_T_DOUBLE:
86 return &ffi_type_double;
87 }
88 }
89 static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
90 return psi_ffi_type(real_decl_type(type)->type);
91 }
92 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
93 if (darg->var->pointer_level) {
94 return &ffi_type_pointer;
95 } else {
96 return psi_ffi_decl_type(darg->type);
97 }
98 }
99
100 typedef struct PSI_LibffiContext {
101 ffi_cif signature;
102 ffi_type *params[2];
103 struct {
104 struct PSI_LibffiData **list;
105 size_t count;
106 } data;
107 } PSI_LibffiContext;
108
109 typedef struct PSI_LibffiData {
110 PSI_LibffiContext *context;
111 impl *impl;
112 zend_internal_arg_info *arginfo;
113 void *code;
114 ffi_closure *closure;
115 ffi_cif signature;
116 ffi_type *params[1];
117 } PSI_LibffiData;
118
119 static inline PSI_LibffiData *PSI_LibffiDataAlloc(PSI_LibffiContext *context, impl *impl) {
120 ffi_status rc;
121 size_t i, c = impl->decl->args ? impl->decl->args->count : 0;
122 PSI_LibffiData *data = malloc(sizeof(*data) + c * sizeof(ffi_type *));
123
124 data->context = context;
125 data->impl = impl;
126 data->arginfo = psi_internal_arginfo(impl);
127 for (i = 0; i < c; ++i) {
128 data->params[i] = psi_ffi_decl_arg_type(impl->decl->args->args[i]);
129 }
130 data->params[c] = NULL;
131
132 rc = ffi_prep_cif(
133 &data->signature,
134 psi_ffi_abi(data->impl->decl->abi->convention),
135 c,
136 psi_ffi_decl_arg_type(data->impl->decl->func),
137 data->params);
138 ZEND_ASSERT(FFI_OK == rc);
139
140 data->closure = psi_ffi_closure_alloc(sizeof(ffi_closure), &data->code);
141 ZEND_ASSERT(data->closure != NULL);
142 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
143 rc = ffi_prep_closure_loc(
144 data->closure,
145 &context->signature,
146 psi_ffi_handler,
147 data,
148 data->code);
149 ZEND_ASSERT(FFI_OK == rc);
150 #elif PSI_HAVE_FFI_PREP_CLOSURE
151 rc = ffi_prep_closure(data->code, &context->signature, psi_ffi_handler, data);
152 ZEND_ASSERT(FFI_OK == rc);
153 #else
154 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() available"
155 #endif
156
157 context->data.list = realloc(context->data.list, ++context->data.count * sizeof(*context->data.list));
158 context->data.list[context->data.count-1] = data;
159
160 return data;
161 }
162
163 static inline void PSI_LibffiDataFree(PSI_LibffiData *data) {
164 psi_ffi_closure_free(data->closure);
165 free(data->arginfo);
166 free(data);
167 }
168
169 static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
170 ffi_status rc;
171
172 if (!L) {
173 L = malloc(sizeof(*L));
174 }
175 memset(L, 0, sizeof(*L));
176
177 L->params[0] = &ffi_type_pointer;
178 L->params[1] = &ffi_type_pointer;
179 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
180 ZEND_ASSERT(rc == FFI_OK);
181
182 return L;
183 }
184
185 static inline void PSI_LibffiContextDtor(PSI_LibffiContext *L) {
186 size_t i;
187
188 for (i = 0; i < L->data.count; ++i) {
189 PSI_LibffiDataFree(L->data.list[i]);
190 }
191 if (L->data.list) {
192 free(L->data.list);
193 }
194 }
195
196 static inline void PSI_LibffiContextFree(PSI_LibffiContext **L) {
197 if (*L) {
198 PSI_LibffiContextDtor(*L);
199 free(*L);
200 *L = NULL;
201 }
202 }
203
204 static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
205 {
206 PSI_LibffiData *data = _data;
207 size_t i;
208 void **arg_prm = NULL;
209 impl_val ret_val;
210
211 if (SUCCESS != psi_parse_args(*(zend_execute_data **)_args[0], data->impl)) {
212 return;
213 }
214
215 if (data->impl->decl->args) {
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_prm[i] = psi_do_let(darg);
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 if (set->arg->_zv) {
233 psi_do_set(set->arg->_zv, set->val->func, set->val->vars);
234 }
235 }
236
237 for (i = 0; i < data->impl->stmts->fre.count; ++i) {
238 free_stmt *fre = data->impl->stmts->fre.list[i];
239
240 psi_do_free(fre);
241 }
242
243 psi_do_clean(data->impl);
244
245 if (arg_prm) {
246 free(arg_prm);
247 }
248 }
249
250 static void psi_ffi_init(PSI_Context *C)
251 {
252 C->context = PSI_LibffiContextInit(NULL);
253 }
254
255 static void psi_ffi_dtor(PSI_Context *C)
256 {
257 PSI_LibffiContextFree((void *) &C->context);
258 }
259
260 static zend_function_entry *psi_ffi_compile(PSI_Context *C, PSI_Data *D)
261 {
262 size_t i, j = 0;
263 zend_function_entry *zfe = calloc(D->impls->count + 1, sizeof(*zfe));
264 PSI_LibffiContext *ctx = C->context;
265
266 for (i = 0; i < D->impls->count; ++i) {
267 zend_function_entry *zf = &zfe[j];
268 PSI_LibffiData *data;
269
270 if (!D->impls->list[i]->decl) {
271 continue;
272 }
273
274 data = PSI_LibffiDataAlloc(ctx, D->impls->list[i]);
275 zf->fname = D->impls->list[i]->func->name + (D->impls->list[i]->func->name[0] == '\\');
276 zf->num_args = D->impls->list[i]->func->args->count;
277 zf->handler = data->code;
278 zf->arg_info = data->arginfo;
279 ++j;
280 }
281
282 return zfe;
283 }
284
285 static PSI_ContextOps ops = {
286 psi_ffi_init,
287 psi_ffi_dtor,
288 psi_ffi_compile,
289 };
290
291 PSI_ContextOps *PSI_Libffi(void)
292 {
293 return &ops;
294 }