fix warning
[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_ARG_INFO(1, errcnt)
190 ZEND_END_ARG_INFO();
191 static PHP_FUNCTION(psi_validate)
192 {
193 zend_string *file;
194 struct psi_parser_input *I;
195 struct psi_parser P;
196 struct psi_data D = {0};
197 struct psi_validate_scope S = {0};
198 zend_long flags = 0;
199 zval *errcnt = NULL;
200
201 #if PHP_DEBUG
202 if (psi_check_env("PSI_DEBUG")) {
203 flags |= PSI_DEBUG;
204 }
205 #endif
206
207 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|lz", &file, &flags, &errcnt)) {
208 return;
209 }
210
211 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
212 RETURN_FALSE;
213 }
214 if (!(I = psi_parser_open_file(&P, file->val, true))) {
215 psi_parser_dtor(&P);
216 RETURN_FALSE;
217 }
218
219 psi_parser_parse(&P, I);
220 psi_data_ctor(&D, P.error, P.flags);
221 psi_validate_scope_ctor(&S);
222 S.defs = &P.preproc->defs;
223
224 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
225
226 if (errcnt) {
227 ZVAL_DEREF(errcnt);
228 convert_to_long(errcnt);
229 ZVAL_LONG(errcnt, P.errors);
230 }
231
232 psi_validate_scope_dtor(&S);
233 psi_data_dtor(&D);
234 psi_parser_dtor(&P);
235 free(I);
236 }
237
238 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
239 ZEND_ARG_INFO(0, string)
240 ZEND_ARG_INFO(0, flags)
241 ZEND_ARG_INFO(1, errcnt)
242 ZEND_END_ARG_INFO();
243 static PHP_FUNCTION(psi_validate_string)
244 {
245 zend_string *string;
246 struct psi_parser_input *I;
247 struct psi_parser P;
248 struct psi_data D = {0};
249 struct psi_validate_scope S = {0};
250 zend_long flags = 0;
251 zval *errcnt = NULL;
252
253 #if PHP_DEBUG
254 if (psi_check_env("PSI_DEBUG")) {
255 flags |= PSI_DEBUG;
256 }
257 #endif
258
259 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|lz", &string, &flags, &errcnt)) {
260 return;
261 }
262
263 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
264 RETURN_FALSE;
265 }
266 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
267 psi_parser_dtor(&P);
268 RETURN_FALSE;
269 }
270
271 psi_parser_parse(&P, I);
272 psi_data_ctor(&D, P.error, P.flags);
273 psi_validate_scope_ctor(&S);
274 S.defs = &P.preproc->defs;
275
276 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
277
278 if (errcnt) {
279 ZVAL_DEREF(errcnt);
280 convert_to_long(errcnt);
281 ZVAL_LONG(errcnt, P.errors);
282 }
283
284 psi_validate_scope_dtor(&S);
285 psi_data_dtor(&D);
286 psi_parser_dtor(&P);
287 free(I);
288 }
289
290 static ZEND_RESULT_CODE psi_ops_load()
291 {
292 struct psi_context_ops *ops = NULL;
293 #ifdef HAVE_LIBJIT
294 if (!strcasecmp(PSI_G(engine), "jit")) {
295 ops = psi_libjit_ops();
296 } else
297 #endif
298 #ifdef HAVE_LIBFFI
299 ops = psi_libffi_ops();
300 #endif
301
302 if (!ops) {
303 php_error(E_WARNING, "No PSI engine found");
304 return FAILURE;
305 }
306
307 PSI_G(ops) = ops;
308 if (ops->load) {
309 return ops->load();
310 }
311 return SUCCESS;
312 }
313
314 static PHP_MINIT_FUNCTION(psi)
315 {
316 zend_class_entry ce = {0};
317 unsigned flags = 0;
318
319 REGISTER_INI_ENTRIES();
320
321 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
322 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
323
324 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
325 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
326 psi_class_entry->create_object = psi_object_init;
327
328 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
329 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
330 psi_object_handlers.free_obj = psi_object_free;
331 psi_object_handlers.clone_obj = NULL;
332
333 if (SUCCESS != psi_ops_load()) {
334 return FAILURE;
335 }
336
337 if (psi_check_env("PSI_DEBUG")) {
338 flags |= PSI_DEBUG;
339 }
340 if (psi_check_env("PSI_SILENT")) {
341 flags |= PSI_SILENT;
342 }
343
344 PSI_G(search_path) = pemalloc(strlen(PSI_G(directory)) + strlen(psi_cpp_search) + 1 + 1, 1);
345 sprintf(PSI_G(search_path), "%s:%s", PSI_G(directory), psi_cpp_search);
346
347 PSI_G(context) = psi_context_init(NULL, PSI_G(ops), psi_error_wrapper, flags);
348 psi_context_build(PSI_G(context), PSI_G(directory));
349
350 return SUCCESS;
351 }
352
353 static PHP_MSHUTDOWN_FUNCTION(psi)
354 {
355 if (psi_check_env("PSI_DUMP")) {
356 psi_context_dump(PSI_G(context), STDOUT_FILENO);
357 }
358
359 psi_context_free(&PSI_G(context));
360
361 if (PSI_G(ops)->free) {
362 PSI_G(ops)->free();
363 }
364
365 UNREGISTER_INI_ENTRIES();
366
367 return SUCCESS;
368 }
369
370 #if defined(COMPILE_DL_PSI) && defined(ZTS)
371 static PHP_RINIT_FUNCTION(psi)
372 {
373 ZEND_TSRMLS_CACHE_UPDATE();
374 return SUCCESS;
375 }
376 #endif
377
378 static PHP_MINFO_FUNCTION(psi)
379 {
380 php_info_print_table_start();
381 php_info_print_table_header(2, "PSI Support", "enabled");
382 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
383 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
384 php_info_print_table_end();
385
386 php_info_print_table_start();
387 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
388 php_info_print_table_row(3, "libffi",
389 #ifndef PHP_PSI_LIBFFI_VERSION
390 # define PHP_PSI_LIBFFI_VERSION "unknown"
391 #endif
392 #ifdef HAVE_LIBFFI
393 PHP_PSI_LIBFFI_VERSION, "unknown"
394 #else
395 "disabled", "disabled"
396 #endif
397 );
398 php_info_print_table_row(3, "libjit",
399 #ifdef HAVE_LIBJIT
400 "unknown", "unknown"
401 #else
402 "disabled", "disabled"
403 #endif
404 );
405
406 DISPLAY_INI_ENTRIES();
407 }
408
409 static void ptr_free(void *ptr)
410 {
411 free(*(void **) ptr);
412 }
413
414 static void psi_blacklist_add_decl(const char *pattern, size_t len)
415 {
416 char *tmp = strndup(pattern, len);
417 struct psi_plist **decls = &PSI_G(blacklist).decls;
418
419 *decls = psi_plist_add(*decls, &tmp);
420 }
421
422 static void psi_blacklist_add_var(const char *pattern, size_t len)
423 {
424 char *tmp = strndup(pattern, len);
425 struct psi_plist **vars = &PSI_G(blacklist).vars;
426
427 *vars = psi_plist_add(*vars, &tmp);
428 }
429
430 static PHP_GINIT_FUNCTION(psi)
431 {
432 char *tmp;
433 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
434 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
435
436 *bl_decls = psi_plist_init(ptr_free);
437 *bl_vars = psi_plist_init(ptr_free);
438
439 #define BL_ADD(D, d) \
440 tmp = strdup(d); \
441 *D = psi_plist_add(*D, &tmp)
442 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
443 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
444
445 BL_DECL_ADD("dlsym");
446 BL_DECL_ADD("alloca");
447 BL_DECL_ADD("atexit");
448 BL_DECL_ADD("at_quick_exit");
449
450 /* missing */
451 BL_DECL_ADD("_IO_cookie_init");
452 BL_DECL_ADD("bindresvport6");
453
454 /* va_list as arg */
455 BL_DECL_ADD("*v*printf");
456 BL_DECL_ADD("*v*scanf");
457 BL_DECL_ADD("vsyslog");
458
459 /* LFS/LFO for 32bit */
460 BL_DECL_ADD("*stat*64");
461 BL_DECL_ADD("*glob*64");
462 /* Hurd only */
463 BL_DECL_ADD("getumask");
464
465 /* using hidden structs */
466 BL_VAR_ADD("_IO_2_*");
467 }
468
469 static PHP_GSHUTDOWN_FUNCTION(psi)
470 {
471 psi_plist_free(psi_globals->blacklist.decls);
472 }
473
474 static const zend_function_entry psi_functions[] = {
475 PHP_FE(psi_dump, ai_psi_dump)
476 PHP_FE(psi_validate, ai_psi_validate)
477 PHP_FE(psi_validate_string, ai_psi_validate_string)
478 PHP_FE_END
479 };
480
481 static const zend_module_dep psi_deps[] = {
482 ZEND_MOD_REQUIRED("standard")
483 {0}
484 };
485
486 zend_module_entry psi_module_entry = {
487 STANDARD_MODULE_HEADER_EX,
488 NULL,
489 psi_deps,
490 "psi",
491 psi_functions,
492 PHP_MINIT(psi),
493 PHP_MSHUTDOWN(psi),
494 #if defined(COMPILE_DL_PSI) && defined(ZTS)
495 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
496 #else
497 NULL,
498 #endif
499 NULL,
500 PHP_MINFO(psi),
501 PHP_PSI_VERSION,
502 ZEND_MODULE_GLOBALS(psi),
503 PHP_GINIT(psi),
504 PHP_GSHUTDOWN(psi),
505 NULL, /* post-deactivate */
506 STANDARD_MODULE_PROPERTIES_EX
507 };
508
509 #ifdef COMPILE_DL_PSI
510 #ifdef ZTS
511 ZEND_TSRMLS_CACHE_DEFINE();
512 #endif
513 ZEND_GET_MODULE(psi)
514 #endif
515
516 /*
517 * Local variables:
518 * tab-width: 4
519 * c-basic-offset: 4
520 * End:
521 * vim600: noet sw=4 ts=4 fdm=marker
522 * vim<600: noet sw=4 ts=4
523 */