5ff4b50e3b4ef5035a468101f00012b892908fb5
[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 #include "cpp.h"
38
39 #define PSI_CPP_SEARCH
40 #include "php_psi_cpp.h"
41
42 #if HAVE_LIBJIT
43 # include "libjit.h"
44 # ifndef HAVE_LIBFFI
45 # define PSI_ENGINE "jit"
46 # endif
47 #endif
48 #if HAVE_LIBFFI
49 # include "libffi.h"
50 # define PSI_ENGINE "ffi"
51 #endif
52
53 ZEND_DECLARE_MODULE_GLOBALS(psi);
54
55 //#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)
56
57 static void OnUpdateBlacklist(const char *str, void (*cb)(const char*, size_t))
58 {
59 const char *end;
60
61 do {
62 size_t len;
63
64 end = strchr(str, ',');
65 if (end) {
66 len = end - str;
67 } else {
68 len = strlen(str);
69 }
70 if (len) {
71 cb(str, len);
72 }
73
74 str = end + 1;
75 } while (end);
76 }
77
78 static void psi_blacklist_add_decl(const char *pattern, size_t len);
79 static ZEND_INI_MH(OnUpdateBlacklistedDecls)
80 {
81 OnUpdateBlacklist(new_value->val, psi_blacklist_add_decl);
82 return SUCCESS;
83 }
84
85 static void psi_blacklist_add_var(const char *pattern, size_t len);
86 static ZEND_INI_MH(OnUpdateBlacklistedVars)
87 {
88 OnUpdateBlacklist(new_value->val, psi_blacklist_add_var);
89 return SUCCESS;
90 }
91
92 static void OnDisplayBlacklist(struct psi_plist *bl)
93 {
94 size_t i = 0;
95 char *item;
96
97 while (psi_plist_get(bl, i++, &item)) {
98 if (i > 1) {
99 PUTS(",");
100 }
101 PUTS(item);
102 }
103 }
104 static ZEND_INI_DISP(OnDisplayBlacklistedDecls)
105 {
106 OnDisplayBlacklist(PSI_G(blacklist).decls);
107 }
108 static ZEND_INI_DISP(OnDisplayBlacklistedVars)
109 {
110 OnDisplayBlacklist(PSI_G(blacklist).vars);
111 }
112
113 PHP_INI_BEGIN()
114 STD_PHP_INI_ENTRY("psi.engine", PSI_ENGINE, PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
115 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
116 PHP_INI_ENTRY_EX("psi.blacklist.decls", "", PHP_INI_SYSTEM, OnUpdateBlacklistedDecls, OnDisplayBlacklistedDecls)
117 PHP_INI_ENTRY_EX("psi.blacklist.vars", "", PHP_INI_SYSTEM, OnUpdateBlacklistedVars, OnDisplayBlacklistedVars)
118 PHP_INI_END();
119
120 static zend_object_handlers psi_object_handlers;
121 static zend_class_entry *psi_class_entry;
122
123 zend_class_entry *psi_object_get_class_entry()
124 {
125 return psi_class_entry;
126 }
127
128 static void psi_object_free(zend_object *o)
129 {
130 psi_object *obj = PSI_OBJ(NULL, o);
131
132 if (obj->data) {
133 if (obj->dtor) {
134 obj->dtor(obj->data);
135 }
136 obj->data = NULL;
137 }
138 zend_object_std_dtor(o);
139 }
140
141 zend_object *psi_object_init_ex(zend_class_entry *ce, void *data, void (*dtor)(void *))
142 {
143 psi_object *o;
144
145 if (!ce) {
146 ce = psi_class_entry;
147 }
148
149 o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
150
151 o->data = data;
152 o->dtor = dtor;
153
154 zend_object_std_init(&o->std, ce);
155 object_properties_init(&o->std, ce);
156 o->std.handlers = &psi_object_handlers;
157 return &o->std;
158 }
159
160 zend_object *psi_object_init(zend_class_entry *ce)
161 {
162 return psi_object_init_ex(ce, NULL, NULL);
163 }
164
165 ZEND_BEGIN_ARG_INFO_EX(ai_psi_dump, 0, 0, 0)
166 ZEND_ARG_INFO(0, stream)
167 ZEND_END_ARG_INFO();
168 static PHP_FUNCTION(psi_dump)
169 {
170 php_stream *s;
171 zval *r = NULL;
172 int fd = STDOUT_FILENO;
173
174 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &r)) {
175 return;
176 }
177 if (r) {
178 php_stream_from_zval(s, r);
179
180 if (SUCCESS != php_stream_cast(s, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void **)&fd, 1)) {
181 RETURN_FALSE;
182 }
183 }
184 psi_context_dump(PSI_G(context), fd);
185 }
186
187 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate, 0, 0, 1)
188 ZEND_ARG_INFO(0, file)
189 ZEND_ARG_INFO(0, flags)
190 ZEND_ARG_INFO(1, errcnt)
191 ZEND_END_ARG_INFO();
192 static PHP_FUNCTION(psi_validate)
193 {
194 zend_string *file;
195 struct psi_parser_input *I;
196 struct psi_parser P;
197 struct psi_data D = {0};
198 struct psi_validate_scope S = {0};
199 zend_long flags = 0;
200 zval *errcnt = NULL;
201
202 #if PHP_DEBUG
203 if (psi_check_env("PSI_DEBUG")) {
204 flags |= PSI_DEBUG;
205 }
206 #endif
207
208 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "P|lz", &file, &flags, &errcnt)) {
209 return;
210 }
211
212 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
213 RETURN_FALSE;
214 }
215 if (!(I = psi_parser_open_file(&P, file->val, true))) {
216 psi_parser_dtor(&P);
217 RETURN_FALSE;
218 }
219
220 psi_parser_parse(&P, I);
221 psi_data_ctor(&D, P.error, P.flags);
222 psi_validate_scope_ctor(&S);
223 S.defs = &P.preproc->defs;
224
225 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
226
227 if (errcnt) {
228 ZVAL_DEREF(errcnt);
229 convert_to_long(errcnt);
230 ZVAL_LONG(errcnt, P.errors);
231 }
232
233 psi_validate_scope_dtor(&S);
234 psi_data_dtor(&D);
235 psi_parser_dtor(&P);
236 psi_parser_input_free(&I);
237 }
238
239 ZEND_BEGIN_ARG_INFO_EX(ai_psi_validate_string, 0, 0, 1)
240 ZEND_ARG_INFO(0, string)
241 ZEND_ARG_INFO(0, flags)
242 ZEND_ARG_INFO(1, errcnt)
243 ZEND_END_ARG_INFO();
244 static PHP_FUNCTION(psi_validate_string)
245 {
246 zend_string *string;
247 struct psi_parser_input *I;
248 struct psi_parser P;
249 struct psi_data D = {0};
250 struct psi_validate_scope S = {0};
251 zend_long flags = 0;
252 zval *errcnt = NULL;
253
254 #if PHP_DEBUG
255 if (psi_check_env("PSI_DEBUG")) {
256 flags |= PSI_DEBUG;
257 }
258 #endif
259
260 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|lz", &string, &flags, &errcnt)) {
261 return;
262 }
263
264 if (!psi_parser_init(&P, psi_error_wrapper, flags)) {
265 RETURN_FALSE;
266 }
267 if (!(I = psi_parser_open_string(&P, string->val, string->len))) {
268 psi_parser_dtor(&P);
269 RETURN_FALSE;
270 }
271
272 psi_parser_parse(&P, I);
273 psi_data_ctor(&D, P.error, P.flags);
274 psi_validate_scope_ctor(&S);
275 S.defs = &P.preproc->defs;
276
277 RETVAL_BOOL(psi_validate(&S, &D, PSI_DATA(&P)));
278
279 if (errcnt) {
280 ZVAL_DEREF(errcnt);
281 convert_to_long(errcnt);
282 ZVAL_LONG(errcnt, P.errors);
283 }
284
285 psi_validate_scope_dtor(&S);
286 psi_data_dtor(&D);
287 psi_parser_dtor(&P);
288 psi_parser_input_free(&I);
289 }
290
291 static ZEND_RESULT_CODE psi_ops_load()
292 {
293 struct psi_context_ops *ops = NULL;
294 #ifdef HAVE_LIBJIT
295 if (!strcasecmp(PSI_G(engine), "jit")) {
296 ops = psi_libjit_ops();
297 } else
298 #endif
299 #ifdef HAVE_LIBFFI
300 ops = psi_libffi_ops();
301 #endif
302
303 if (!ops) {
304 php_error(E_WARNING, "No PSI engine found");
305 return FAILURE;
306 }
307
308 PSI_G(ops) = ops;
309 if (ops->load) {
310 return ops->load();
311 }
312 return SUCCESS;
313 }
314
315 PHP_MINIT_FUNCTION(psi_cpp);
316 static PHP_MINIT_FUNCTION(psi)
317 {
318 zend_class_entry ce = {0};
319 unsigned flags = 0;
320
321 REGISTER_INI_ENTRIES();
322
323 zend_register_long_constant(ZEND_STRL("PSI_DEBUG"), PSI_DEBUG, CONST_CS|CONST_PERSISTENT, module_number);
324 zend_register_long_constant(ZEND_STRL("PSI_SILENT"), PSI_SILENT, CONST_CS|CONST_PERSISTENT, module_number);
325
326 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
327 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
328 psi_class_entry->create_object = psi_object_init;
329
330 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
331 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
332 psi_object_handlers.free_obj = psi_object_free;
333 psi_object_handlers.clone_obj = NULL;
334
335 if (SUCCESS != PHP_MINIT(psi_cpp)(type, module_number)) {
336 return FAILURE;
337 }
338
339 if (SUCCESS != psi_ops_load()) {
340 return FAILURE;
341 }
342
343 if (psi_check_env("PSI_DEBUG")) {
344 flags |= PSI_DEBUG;
345 }
346 if (psi_check_env("PSI_SILENT")) {
347 flags |= PSI_SILENT;
348 }
349
350 PSI_G(search_path) = pemalloc(strlen(PSI_G(directory)) + strlen(psi_cpp_search) + 1 + 1, 1);
351 sprintf(PSI_G(search_path), "%s:%s", PSI_G(directory), psi_cpp_search);
352
353 PSI_G(context) = psi_context_init(NULL, PSI_G(ops), psi_error_wrapper, flags);
354 psi_context_build(PSI_G(context), PSI_G(directory));
355
356 return SUCCESS;
357 }
358
359 PHP_MSHUTDOWN_FUNCTION(psi_cpp);
360 static PHP_MSHUTDOWN_FUNCTION(psi)
361 {
362 if (psi_check_env("PSI_DUMP")) {
363 psi_context_dump(PSI_G(context), STDOUT_FILENO);
364 }
365
366 psi_context_free(&PSI_G(context));
367
368 if (PSI_G(ops)->free) {
369 PSI_G(ops)->free();
370 }
371
372 PHP_MSHUTDOWN(psi_cpp)(type, module_number);
373
374 UNREGISTER_INI_ENTRIES();
375
376 return SUCCESS;
377 }
378
379 #if defined(COMPILE_DL_PSI) && defined(ZTS)
380 static PHP_RINIT_FUNCTION(psi)
381 {
382 ZEND_TSRMLS_CACHE_UPDATE();
383 return SUCCESS;
384 }
385 #endif
386
387 static PHP_MINFO_FUNCTION(psi)
388 {
389 php_info_print_table_start();
390 php_info_print_table_header(2, "PSI Support", "enabled");
391 php_info_print_table_row(2, "Extension Version", PHP_PSI_VERSION);
392 php_info_print_table_row(2, "Search Path", PSI_G(search_path));
393 php_info_print_table_end();
394
395 php_info_print_table_start();
396 php_info_print_table_header(3, "Used Library", "Compiled", "Linked");
397 php_info_print_table_row(3, "libffi",
398 #ifndef PHP_PSI_LIBFFI_VERSION
399 # define PHP_PSI_LIBFFI_VERSION "unknown"
400 #endif
401 #ifdef HAVE_LIBFFI
402 PHP_PSI_LIBFFI_VERSION, "unknown"
403 #else
404 "disabled", "disabled"
405 #endif
406 );
407 php_info_print_table_row(3, "libjit",
408 #ifdef HAVE_LIBJIT
409 "unknown", "unknown"
410 #else
411 "disabled", "disabled"
412 #endif
413 );
414
415 DISPLAY_INI_ENTRIES();
416 }
417
418 static void ptr_free(void *ptr)
419 {
420 free(*(void **) ptr);
421 }
422
423 static void psi_blacklist_add_decl(const char *pattern, size_t len)
424 {
425 char *tmp = strndup(pattern, len);
426 struct psi_plist **decls = &PSI_G(blacklist).decls;
427
428 *decls = psi_plist_add(*decls, &tmp);
429 }
430
431 static void psi_blacklist_add_var(const char *pattern, size_t len)
432 {
433 char *tmp = strndup(pattern, len);
434 struct psi_plist **vars = &PSI_G(blacklist).vars;
435
436 *vars = psi_plist_add(*vars, &tmp);
437 }
438
439 static PHP_GINIT_FUNCTION(psi)
440 {
441 char *tmp;
442 struct psi_plist **bl_decls = &psi_globals->blacklist.decls;
443 struct psi_plist **bl_vars = &psi_globals->blacklist.vars;
444
445 *bl_decls = psi_plist_init(ptr_free);
446 *bl_vars = psi_plist_init(ptr_free);
447
448 #define BL_ADD(D, d) \
449 tmp = strdup(d); \
450 *D = psi_plist_add(*D, &tmp)
451 #define BL_DECL_ADD(d) BL_ADD(bl_decls, d)
452 #define BL_VAR_ADD(d) BL_ADD(bl_vars, d)
453
454 BL_DECL_ADD("dlsym");
455 BL_DECL_ADD("alloca");
456 BL_DECL_ADD("atexit");
457 BL_DECL_ADD("at_quick_exit");
458
459 /* missing */
460 BL_DECL_ADD("_IO_cookie_init");
461 BL_DECL_ADD("bindresvport6");
462
463 /* va_list as arg */
464 BL_DECL_ADD("*v*printf");
465 BL_DECL_ADD("*v*scanf");
466 BL_DECL_ADD("vsyslog");
467
468 /* LFS/LFO for 32bit */
469 BL_DECL_ADD("*stat*64");
470 BL_DECL_ADD("*glob*64");
471 /* Hurd only */
472 BL_DECL_ADD("getumask");
473
474 /* using hidden structs */
475 BL_VAR_ADD("_IO_2_*");
476 }
477
478 static PHP_GSHUTDOWN_FUNCTION(psi)
479 {
480 psi_plist_free(psi_globals->blacklist.decls);
481 }
482
483 static const zend_function_entry psi_functions[] = {
484 PHP_FE(psi_dump, ai_psi_dump)
485 PHP_FE(psi_validate, ai_psi_validate)
486 PHP_FE(psi_validate_string, ai_psi_validate_string)
487 PHP_FE_END
488 };
489
490 static const zend_module_dep psi_deps[] = {
491 ZEND_MOD_REQUIRED("standard")
492 {0}
493 };
494
495 zend_module_entry psi_module_entry = {
496 STANDARD_MODULE_HEADER_EX,
497 NULL,
498 psi_deps,
499 "psi",
500 psi_functions,
501 PHP_MINIT(psi),
502 PHP_MSHUTDOWN(psi),
503 #if defined(COMPILE_DL_PSI) && defined(ZTS)
504 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
505 #else
506 NULL,
507 #endif
508 NULL,
509 PHP_MINFO(psi),
510 PHP_PSI_VERSION,
511 ZEND_MODULE_GLOBALS(psi),
512 PHP_GINIT(psi),
513 PHP_GSHUTDOWN(psi),
514 NULL, /* post-deactivate */
515 STANDARD_MODULE_PROPERTIES_EX
516 };
517
518 #ifdef COMPILE_DL_PSI
519 #ifdef ZTS
520 ZEND_TSRMLS_CACHE_DEFINE();
521 #endif
522 ZEND_GET_MODULE(psi)
523 #endif
524
525 /*
526 * Local variables:
527 * tab-width: 4
528 * c-basic-offset: 4
529 * End:
530 * vim600: noet sw=4 ts=4 fdm=marker
531 * vim<600: noet sw=4 ts=4
532 */