ifdef HAVE_INT128
[m6w6/ext-psi] / src / module.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27
28 #include "php.h"
29 #include "php_ini.h"
30 #include "ext/standard/info.h"
31 #include "zend_constants.h"
32 #include "zend_operators.h"
33
34 #include "php_psi.h"
35 #include "token.h"
36 #include "parser.h"
37
38 #define PSI_CPP_SEARCH
39 #include "php_psi_cpp.h"
40
41 #if HAVE_LIBJIT
42 # include "libjit.h"
43 # ifndef HAVE_LIBFFI
44 # define PSI_ENGINE "jit"
45 # endif
46 #endif
47 #if HAVE_LIBFFI
48 # include "libffi.h"
49 # define PSI_ENGINE "ffi"
50 #endif
51
52 ZEND_DECLARE_MODULE_GLOBALS(psi);
53
54 //#define ZEND_INI_MH(name) int name(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage)
55
56 static void OnUpdateBlacklist(const char *str, void (*cb)(const char*, size_t))
57 {
58 const char *end;
59
60 do {
61 size_t len;
62
63 end = strchr(str, ',');
64 if (end) {
65 len = end - str;
66 } else {
67 len = strlen(str);
68 }
69 if (len) {
70 cb(str, len);
71 }
72
73 str = end + 1;
74 } while (end);
75 }
76
77 static void psi_blacklist_add_decl(const char *pattern, size_t len);
78 static ZEND_INI_MH(OnUpdateBlacklistedDecls)
79 {
80 OnUpdateBlacklist(new_value->val, psi_blacklist_add_decl);
81 return SUCCESS;
82 }
83
84 static void psi_blacklist_add_var(const char *pattern, size_t len);
85 static ZEND_INI_MH(OnUpdateBlacklistedVars)
86 {
87 OnUpdateBlacklist(new_value->val, psi_blacklist_add_var);
88 return SUCCESS;
89 }
90
91 static void OnDisplayBlacklist(struct psi_plist *bl)
92 {
93 size_t i = 0;
94 char *item;
95
96 while (psi_plist_get(bl, i++, &item)) {
97 if (i > 1) {
98 PUTS(",");
99 }
100 PUTS(item);
101 }
102 }
103 static ZEND_INI_DISP(OnDisplayBlacklistedDecls)
104 {
105 OnDisplayBlacklist(PSI_G(blacklist).decls);
106 }
107 static ZEND_INI_DISP(OnDisplayBlacklistedVars)
108 {
109 OnDisplayBlacklist(PSI_G(blacklist).vars);
110 }
111
112 PHP_INI_BEGIN()
113 STD_PHP_INI_ENTRY("psi.engine", PSI_ENGINE, PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
114 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
115 PHP_INI_ENTRY_EX("psi.blacklist.decls", "", PHP_INI_SYSTEM, OnUpdateBlacklistedDecls, OnDisplayBlacklistedDecls)
116 PHP_INI_ENTRY_EX("psi.blacklist.vars", "", PHP_INI_SYSTEM, OnUpdateBlacklistedVars, OnDisplayBlacklistedVars)
117 PHP_INI_END();
118
119 static zend_object_handlers psi_object_handlers;
120 static zend_class_entry *psi_class_entry;
121
122 zend_class_entry *psi_object_get_class_entry()
123 {
124 return psi_class_entry;
125 }
126
127 static void psi_object_free(zend_object *o)
128 {
129 psi_object *obj = PSI_OBJ(NULL, o);
130
131 if (obj->data) {
132 if (obj->dtor) {
133 obj->dtor(obj->data);
134 }
135 obj->data = NULL;
136 }
137 zend_object_std_dtor(o);
138 }
139
140 zend_object *psi_object_init_ex(zend_class_entry *ce, void *data, void (*dtor)(void *))
141 {
142 psi_object *o;
143
144 if (!ce) {
145 ce = psi_class_entry;
146 }
147
148 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
149
150 o->data = data;
151 o->dtor = dtor;
152
153 zend_object_std_init(&o->std, ce);
154 object_properties_init(&o->std, ce);
155 o->std.handlers = &psi_object_handlers;
156 return &o->std;
157 }
158
159 zend_object *psi_object_init(zend_class_entry *ce)
160 {
161 return psi_object_init_ex(ce, NULL, NULL);
162 }
163
164 ZEND_BEGIN_ARG_INFO_EX(ai_psi_dump, 0, 0, 0)
165 ZEND_ARG_INFO(0, stream)
166 ZEND_END_ARG_INFO();
167 static PHP_FUNCTION(psi_dump)
168 {
169 php_stream *s;
170 zval *r = NULL;
171 int fd = STDOUT_FILENO;
172
173 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &r)) {
174 return;
175 }
176 if (r) {
177 php_stream_from_zval(s, r);
178
179 if (SUCCESS != php_stream_cast(s, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void **)&fd, 1)) {
180 RETURN_FALSE;
181 }
182 }
183 psi_context_dump(PSI_G(context), fd);
184 }
185
186 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
187 ZEND_ARG_INFO(0, file)
188 ZEND_ARG_INFO(0, flags)
189 ZEND_END_ARG_INFO();
190 static PHP_FUNCTION(psi_validate)
191 {
192 zend_string *file;
193 struct psi_parser_input *I;
194 struct psi_parser P;
195 struct psi_data D = {0};
196 struct psi_validate_scope S = {0};
197 zend_long flags = 0;
198
199 #if PHP_DEBUG
200 if (psi_check_env("PSI_DEBUG")) {
201 flags |= PSI_DEBUG;
202 }
203 #endif
204
205 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &file, &flags)) {
206 return;
207 }
208
209 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
210 RETURN_FALSE;
211 }
212 if (!(I = psi_parser_open_file(&P, file->val, true))) {
213 psi_parser_dtor(&P);
214 RETURN_FALSE;
215 }
216
217 psi_parser_parse(&P, I);
218 psi_data_ctor(&D, P.error, P.flags);
219 psi_validate_scope_ctor(&S);
220 S.defs = &P.preproc->defs;
221 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)) && !P.errors);
222 psi_validate_scope_dtor(&S);
223 psi_data_dtor(&D);
224 psi_parser_dtor(&P);
225 free(I);
226 }
227
228 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
229 ZEND_ARG_INFO(0, string)
230 ZEND_END_ARG_INFO();
231 static PHP_FUNCTION(psi_validate_string)
232 {
233 zend_string *string;
234 struct psi_parser_input *I;
235 struct psi_parser P;
236 struct psi_data D = {0};
237 struct psi_validate_scope S = {0};
238 zend_long flags = 0;
239
240 #if PHP_DEBUG
241 if (psi_check_env("PSI_DEBUG")) {
242 flags |= PSI_DEBUG;
243 }
244 #endif
245
246 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &string, &flags)) {
247 return;
248 }
249
250 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
251 RETURN_FALSE;
252 }
253 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
254 psi_parser_dtor(&P);
255 RETURN_FALSE;
256 }
257
258 psi_parser_parse(&P, I);
259 psi_data_ctor(&D, P.error, P.flags);
260 psi_validate_scope_ctor(&S);
261 S.defs = &P.preproc->defs;
262 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)) && !P.errors);
263 psi_validate_scope_dtor(&S);
264 psi_data_dtor(&D);
265 psi_parser_dtor(&P);
266 free(I);
267 }
268
269 static ZEND_RESULT_CODE psi_ops_load()
270 {
271 struct psi_context_ops *ops = NULL;
272 #ifdef HAVE_LIBJIT
273 if (!strcasecmp(PSI_G(engine), "jit")) {
274 ops = psi_libjit_ops();
275 } else
276 #endif
277 #ifdef HAVE_LIBFFI
278 ops = psi_libffi_ops();
279 #endif
280
281 if (!ops) {
282 php_error(E_WARNING, "No PSI engine found");
283 return FAILURE;
284 }
285
286 PSI_G(ops) = ops;
287 if (ops->load) {
288 return ops->load();
289 }
290 return SUCCESS;
291 }
292
293 static PHP_MINIT_FUNCTION(psi)
294 {
295 zend_class_entry ce = {0};
296 unsigned flags = 0;
297
298 REGISTER_INI_ENTRIES();
299
300 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
301 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
302
303 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
304 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
305 psi_class_entry->create_object = psi_object_init;
306
307 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
308 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
309 psi_object_handlers.free_obj = psi_object_free;
310 psi_object_handlers.clone_obj = NULL;
311
312 if (SUCCESS != psi_ops_load()) {
313 return FAILURE;
314 }
315
316 if (psi_check_env("PSI_DEBUG")) {
317 flags |= PSI_DEBUG;
318 }
319 if (psi_check_env("PSI_SILENT")) {
320 flags |= PSI_SILENT;
321 }
322
323 PSI_G(search_path) = pemalloc(strlen(PSI_G(directory)) + strlen(psi_cpp_search) + 1 + 1, 1);
324 sprintf(PSI_G(search_path), "%s:%s", PSI_G(directory), psi_cpp_search);
325
326 PSI_G(context) = psi_context_init(NULL, PSI_G(ops), psi_error_wrapper, flags);
327 psi_context_build(PSI_G(context), PSI_G(directory));
328
329 return SUCCESS;
330 }
331
332 static PHP_MSHUTDOWN_FUNCTION(psi)
333 {
334 if (psi_check_env("PSI_DUMP")) {
335 psi_context_dump(PSI_G(context), STDOUT_FILENO);
336 }
337
338 psi_context_free(&PSI_G(context));
339
340 if (PSI_G(ops)->free) {
341 PSI_G(ops)->free();
342 }
343
344 UNREGISTER_INI_ENTRIES();
345
346 return SUCCESS;
347 }
348
349 #if defined(COMPILE_DL_PSI) && defined(ZTS)
350 static PHP_RINIT_FUNCTION(psi)
351 {
352 ZEND_TSRMLS_CACHE_UPDATE();
353 return SUCCESS;
354 }
355 #endif
356
357 static PHP_MINFO_FUNCTION(psi)
358 {
359 php_info_print_table_start();
360 php_info_print_table_header(2, "PSI Support", "enabled");
361 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
362 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
363 php_info_print_table_end();
364
365 php_info_print_table_start();
366 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
367 php_info_print_table_row(3, "libffi",
368 #ifndef PHP_PSI_LIBFFI_VERSION
369 # define PHP_PSI_LIBFFI_VERSION "unknown"
370 #endif
371 #ifdef HAVE_LIBFFI
372 PHP_PSI_LIBFFI_VERSION, "unknown"
373 #else
374 "disabled", "disabled"
375 #endif
376 );
377 php_info_print_table_row(3, "libjit",
378 #ifdef HAVE_LIBJIT
379 "unknown", "unknown"
380 #else
381 "disabled", "disabled"
382 #endif
383 );
384
385 DISPLAY_INI_ENTRIES();
386 }
387
388 static void ptr_free(void *ptr)
389 {
390 free(*(void **) ptr);
391 }
392
393 static void psi_blacklist_add_decl(const char *pattern, size_t len)
394 {
395 char *tmp = strndup(pattern, len);
396 struct psi_plist **decls = &PSI_G(blacklist).decls;
397
398 *decls = psi_plist_add(*decls, &tmp);
399 }
400
401 static void psi_blacklist_add_var(const char *pattern, size_t len)
402 {
403 char *tmp = strndup(pattern, len);
404 struct psi_plist **vars = &PSI_G(blacklist).vars;
405
406 *vars = psi_plist_add(*vars, &tmp);
407 }
408
409 static PHP_GINIT_FUNCTION(psi)
410 {
411 char *tmp;
412 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
413 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
414
415 *bl_decls = psi_plist_init(ptr_free);
416 *bl_vars = psi_plist_init(ptr_free);
417
418 #define BL_ADD(D, d) \
419 tmp = strdup(d); \
420 *D = psi_plist_add(*D, &tmp)
421 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
422 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
423
424 BL_DECL_ADD("dlsym");
425 BL_DECL_ADD("alloca");
426 BL_DECL_ADD("atexit");
427 BL_DECL_ADD("at_quick_exit");
428
429 /* missing */
430 BL_DECL_ADD("_IO_cookie_init");
431 BL_DECL_ADD("bindresvport6");
432
433 /* va_list as arg */
434 BL_DECL_ADD("*v*printf");
435 BL_DECL_ADD("*v*scanf");
436 BL_DECL_ADD("vsyslog");
437
438 /* LFS/LFO for 32bit */
439 BL_DECL_ADD("*stat*64");
440 BL_DECL_ADD("*glob*64");
441 /* Hurd only */
442 BL_DECL_ADD("getumask");
443
444 /* using hidden structs */
445 BL_VAR_ADD("_IO_2_*");
446 }
447
448 static PHP_GSHUTDOWN_FUNCTION(psi)
449 {
450 psi_plist_free(psi_globals->blacklist.decls);
451 }
452
453 static const zend_function_entry psi_functions[] = {
454 PHP_FE(psi_dump, ai_psi_dump)
455 PHP_FE(psi_validate, ai_psi_validate)
456 PHP_FE(psi_validate_string, ai_psi_validate_string)
457 PHP_FE_END
458 };
459
460 zend_module_entry psi_module_entry = {
461 STANDARD_MODULE_HEADER,
462 "psi",
463 psi_functions,
464 PHP_MINIT(psi),
465 PHP_MSHUTDOWN(psi),
466 #if defined(COMPILE_DL_PSI) && defined(ZTS)
467 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
468 #else
469 NULL,
470 #endif
471 NULL,
472 PHP_MINFO(psi),
473 PHP_PSI_VERSION,
474 ZEND_MODULE_GLOBALS(psi),
475 PHP_GINIT(psi),
476 PHP_GSHUTDOWN(psi),
477 NULL, /* post-deactivate */
478 STANDARD_MODULE_PROPERTIES_EX
479 };
480
481 #ifdef COMPILE_DL_PSI
482 #ifdef ZTS
483 ZEND_TSRMLS_CACHE_DEFINE();
484 #endif
485 ZEND_GET_MODULE(psi)
486 #endif
487
488 /*
489 * Local variables:
490 * tab-width: 4
491 * c-basic-offset: 4
492 * End:
493 * vim600: noet sw=4 ts=4 fdm=marker
494 * vim<600: noet sw=4 ts=4
495 */