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