d802bd1f293ee09aafabb82ff66a4bd4e02abcc0
[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_token_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_token_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 } PSI_LibffiContext;
104
105 typedef struct PSI_LibffiCall {
106 void *code;
107 ffi_closure *closure;
108 ffi_cif signature;
109 void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
110 } PSI_LibffiCall;
111
112 static inline PSI_LibffiCall *PSI_LibffiCallAlloc(PSI_Context *C, decl *decl) {
113 int rc;
114 size_t i, c = decl->args ? decl->args->count : 0;
115 PSI_LibffiCall *call = calloc(1, sizeof(*call) + 2 * c * sizeof(void *));
116
117 for (i = 0; i < c; ++i) {
118 call->params[i] = psi_ffi_decl_arg_type(decl->args->args[i]);
119 }
120 call->params[c] = NULL;
121
122 decl->call.info = call;
123 decl->call.args = (void **) &call->params[c+1];
124
125 rc = ffi_prep_cif(&call->signature, psi_ffi_abi(decl->abi->convention),
126 c, psi_ffi_decl_arg_type(decl->func), (ffi_type **) call->params);
127 ZEND_ASSERT(FFI_OK == rc);
128
129 return call;
130 }
131
132 static inline void PSI_LibffiCallInitClosure(PSI_Context *C, PSI_LibffiCall *call, impl *impl) {
133 PSI_LibffiContext *context = C->context;
134 int rc;
135
136 call->closure = psi_ffi_closure_alloc(sizeof(ffi_closure), &call->code);
137 ZEND_ASSERT(call->closure != NULL);
138
139 #if PSI_HAVE_FFI_PREP_CLOSURE_LOC
140 rc = ffi_prep_closure_loc(
141 call->closure,
142 &context->signature,
143 psi_ffi_handler,
144 impl,
145 call->code);
146
147 #elif PSI_HAVE_FFI_PREP_CLOSURE
148 rc = ffi_prep_closure(data->code, &context->signature, psi_ffi_handler, data);
149 #else
150 # error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() available"
151 #endif
152 ZEND_ASSERT(FFI_OK == rc);
153 }
154
155 static inline void PSI_LibffiCallFree(PSI_LibffiCall *call) {
156 if (call->closure) {
157 psi_ffi_closure_free(call->closure);
158 }
159 free(call);
160 }
161
162 static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
163 ffi_status rc;
164
165 if (!L) {
166 L = malloc(sizeof(*L));
167 }
168 memset(L, 0, sizeof(*L));
169
170 L->params[0] = &ffi_type_pointer;
171 L->params[1] = &ffi_type_pointer;
172 rc = ffi_prep_cif(&L->signature, FFI_DEFAULT_ABI, 2, &ffi_type_void, L->params);
173 ZEND_ASSERT(rc == FFI_OK);
174
175 return L;
176 }
177
178 static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
179 {
180 psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
181 }
182
183 static void psi_ffi_init(PSI_Context *C)
184 {
185 C->context = PSI_LibffiContextInit(NULL);
186 }
187
188 static void psi_ffi_dtor(PSI_Context *C) {
189 size_t i;
190
191 for (i = 0; i < C->decls->count; ++i) {
192 decl *decl = C->decls->list[i];
193
194 if (decl->call.info) {
195 PSI_LibffiCallFree(decl->call.info);
196 }
197 }
198 free(C->context);
199 }
200
201 static zend_function_entry *psi_ffi_compile(PSI_Context *C)
202 {
203 size_t i, j = 0;
204 zend_function_entry *zfe;
205
206 if (!C->impls) {
207 return NULL;
208 }
209
210 zfe = calloc(C->impls->count + 1, sizeof(*zfe));
211 for (i = 0; i < C->impls->count; ++i) {
212 zend_function_entry *zf = &zfe[j];
213 PSI_LibffiCall *call;
214 impl *impl = C->impls->list[i];
215
216 if (!impl->decl) {
217 continue;
218 }
219
220 call = PSI_LibffiCallAlloc(C, impl->decl);
221 PSI_LibffiCallInitClosure(C, call, impl);
222
223 zf->fname = impl->func->name + (impl->func->name[0] == '\\');
224 zf->num_args = impl->func->args->count;
225 zf->handler = call->code;
226 zf->arg_info = psi_internal_arginfo(impl);
227 ++j;
228 }
229
230 for (i = 0; i < C->decls->count; ++i) {
231 decl *decl = C->decls->list[i];
232
233 if (decl->impl) {
234 continue;
235 }
236
237 PSI_LibffiCallAlloc(C, decl);
238 }
239
240 return zfe;
241 }
242
243 static void psi_ffi_call(PSI_Context *C, impl_val *ret_val, decl *decl) {
244 PSI_LibffiCall *call = decl->call.info;
245
246 ffi_call(&call->signature, FFI_FN(decl->call.sym), ret_val, decl->call.args);
247 }
248
249 static PSI_ContextOps ops = {
250 psi_ffi_init,
251 psi_ffi_dtor,
252 psi_ffi_compile,
253 psi_ffi_call,
254 };
255
256 PSI_ContextOps *PSI_Libffi(void)
257 {
258 return &ops;
259 }