├── CREDITS ├── EXPERIMENTAL ├── config.w32 ├── opdump.php ├── README.md ├── .gitignore ├── tests └── 001.phpt ├── config.m4 ├── php_opdump.h └── opdump.c /CREDITS: -------------------------------------------------------------------------------- 1 | opdump -------------------------------------------------------------------------------- /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("opdump", "for opdump support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("opdump", "enable opdump support", "no"); 9 | 10 | if (PHP_OPDUMP != "no") { 11 | EXTENSION("opdump", "opdump.c", PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /opdump.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('opdump')) { 5 | dl('opdump.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'opdump'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | echo opdump_test('heheh'); 15 | echo "$str\n"; 16 | ?> 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 简单的PHP7 opcode查看器 2 | 3 | PHP有一个很好的opcode查看器[VLD](https://github.com/derickr/vld)。不过代码比较复杂,不易看懂,为了方便理解,写了这个简单的查看器。 4 | 5 | 代码很简单,基本原理是使用扩展的方式在RINIT的时候修改zend_compile_file函数,将其编译出的opcode文件存放在php文件同目录下, 6 | 容易看清opcode存储的基本原理。 7 | 8 | 支持显示的格式不多,可以根据自己需要添加。 9 | 10 | #### 使用方法 11 | 编译: phpize && ./configure --prefix=$PHP_PREFIX && make install 12 | 13 | 详细参见 [Building extensions using phpize](http://www.phpinternalsbook.com/build_system/building_extensions.html#building-extensions-using-phpize) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | .libs 5 | acinclude.m4 6 | aclocal.m4 7 | autom4te.cache 8 | build 9 | config.guess 10 | config.h 11 | config.h.in 12 | config.log 13 | config.nice 14 | config.status 15 | config.sub 16 | configure 17 | configure.in 18 | include 19 | install-sh 20 | libtool 21 | ltmain.sh 22 | Makefile 23 | Makefile.fragments 24 | Makefile.global 25 | Makefile.objects 26 | missing 27 | mkinstalldirs 28 | modules 29 | run-tests.php 30 | tests/*/*.diff 31 | tests/*/*.out 32 | tests/*/*.php 33 | tests/*/*.exp 34 | tests/*/*.log 35 | tests/*/*.sh 36 | 37 | # eclipse project files 38 | .cproject 39 | .project 40 | .settings 41 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for opdump presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | opdump extension is available 22 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension opdump 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | dnl PHP_ARG_WITH(opdump, for opdump support, 11 | dnl Make sure that the comment is aligned: 12 | dnl [ --with-opdump Include opdump support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | PHP_ARG_ENABLE(opdump, whether to enable opdump support, 17 | Make sure that the comment is aligned: 18 | [ --enable-opdump Enable opdump support]) 19 | 20 | if test "$PHP_OPDUMP" != "no"; then 21 | dnl Write more examples of tests here... 22 | 23 | dnl # --with-opdump -> check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/opdump.h" # you most likely want to change this 26 | dnl if test -r $PHP_OPDUMP/$SEARCH_FOR; then # path given as parameter 27 | dnl OPDUMP_DIR=$PHP_OPDUMP 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for opdump files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl OPDUMP_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$OPDUMP_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the opdump distribution]) 41 | dnl fi 42 | 43 | dnl # --with-opdump -> add include path 44 | dnl PHP_ADD_INCLUDE($OPDUMP_DIR/include) 45 | 46 | dnl # --with-opdump -> check for lib and symbol presence 47 | dnl LIBNAME=opdump # you may want to change this 48 | dnl LIBSYMBOL=opdump # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $OPDUMP_DIR/$PHP_LIBDIR, OPDUMP_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_OPDUMPLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong opdump lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$OPDUMP_DIR/$PHP_LIBDIR -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(OPDUMP_SHARED_LIBADD) 61 | 62 | PHP_NEW_EXTENSION(opdump, opdump.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) 63 | fi 64 | -------------------------------------------------------------------------------- /php_opdump.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_OPDUMP_H 22 | #define PHP_OPDUMP_H 23 | 24 | extern zend_module_entry opdump_module_entry; 25 | #define phpext_opdump_ptr &opdump_module_entry 26 | 27 | #define PHP_OPDUMP_VERSION "0.1.0" /* Replace with version number for your extension */ 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_OPDUMP_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_OPDUMP_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_OPDUMP_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | /* 42 | Declare any global variables you may need between the BEGIN 43 | and END macros here: 44 | 45 | ZEND_BEGIN_MODULE_GLOBALS(opdump) 46 | zend_long global_value; 47 | char *global_string; 48 | ZEND_END_MODULE_GLOBALS(opdump) 49 | */ 50 | 51 | /* Always refer to the globals in your function as OPDUMP_G(variable). 52 | You are encouraged to rename these macros something shorter, see 53 | examples in any other php module directory. 54 | */ 55 | #define OPDUMP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(opdump, v) 56 | 57 | #if defined(ZTS) && defined(COMPILE_DL_OPDUMP) 58 | ZEND_TSRMLS_CACHE_EXTERN(); 59 | #endif 60 | 61 | #endif /* PHP_OPDUMP_H */ 62 | 63 | 64 | /* 65 | * Local variables: 66 | * tab-width: 4 67 | * c-basic-offset: 4 68 | * End: 69 | * vim600: noet sw=4 ts=4 fdm=marker 70 | * vim<600: noet sw=4 ts=4 71 | */ 72 | -------------------------------------------------------------------------------- /opdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_ini.h" 27 | #include "ext/standard/info.h" 28 | #include "php_opdump.h" 29 | #include "zend_vm_opcodes.h" 30 | #include "zend_compile.h" 31 | 32 | /* If you declare any globals in php_opdump.h uncomment this: 33 | ZEND_DECLARE_MODULE_GLOBALS(opdump) 34 | */ 35 | 36 | /* True global resources - no need for thread safety here */ 37 | static int le_opdump; 38 | 39 | /* {{{ PHP_INI 40 | */ 41 | /* Remove comments and fill if you need to have entries in php.ini 42 | PHP_INI_BEGIN() 43 | STD_PHP_INI_ENTRY("opdump.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_opdump_globals, opdump_globals) 44 | STD_PHP_INI_ENTRY("opdump.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_opdump_globals, opdump_globals) 45 | PHP_INI_END() 46 | */ 47 | /* }}} */ 48 | 49 | /* Every user-visible function in PHP should document itself in the source */ 50 | /* {{{ proto string confirm_opdump_compiled(string arg) 51 | Return a string to confirm that the module is compiled in */ 52 | PHP_FUNCTION(opdump_test) { 53 | char *arg = NULL; 54 | size_t arg_len, len; 55 | zend_string *strg; 56 | 57 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) 58 | == FAILURE) { 59 | return; 60 | } 61 | 62 | strg = strpprintf(0, "hehe"); 63 | 64 | RETURN_STR(strg); 65 | } 66 | /* }}} */ 67 | /* The previous line is meant for vim and emacs, so it can correctly fold and 68 | unfold functions in source code. See the corresponding marks just before 69 | function definition, where the functions purpose is also documented. Please 70 | follow this convention for the convenience of others editing your code. 71 | */ 72 | 73 | /* {{{ php_opdump_init_globals 74 | */ 75 | /* Uncomment this function if you have INI entries 76 | static void php_opdump_init_globals(zend_opdump_globals *opdump_globals) 77 | { 78 | opdump_globals->global_value = 0; 79 | opdump_globals->global_string = NULL; 80 | } 81 | */ 82 | /* }}} */ 83 | 84 | /* {{{ PHP_MINIT_FUNCTION 85 | */ 86 | PHP_MINIT_FUNCTION(opdump) { 87 | /* If you have INI entries, uncomment these lines 88 | REGISTER_INI_ENTRIES(); 89 | */ 90 | return SUCCESS; 91 | } 92 | /* }}} */ 93 | 94 | /* {{{ PHP_MSHUTDOWN_FUNCTION 95 | */ 96 | PHP_MSHUTDOWN_FUNCTION(opdump) { 97 | /* uncomment this line if you have INI entries 98 | UNREGISTER_INI_ENTRIES(); 99 | */ 100 | return SUCCESS; 101 | } 102 | /* }}} */ 103 | 104 | ZEND_API zend_op_array *(*origin_zend_compile_file)( 105 | zend_file_handle *file_handle, int type); 106 | ZEND_API zend_op_array *(*origin_zend_compile_string)(zval *source_string, 107 | char *filename); 108 | 109 | FILE* op_file; 110 | size_t opdump_printf(const char *format, ...) { 111 | char * message; 112 | va_list args; 113 | va_start(args, format); 114 | int ret = vspprintf(&message, 0, format, args); 115 | va_end(args); 116 | fprintf(op_file, "%s", message); 117 | efree(message); 118 | return ret; 119 | } 120 | 121 | void format_zval_string(char* buffer, zval*z){ 122 | zend_string* str = Z_STR_P(z); 123 | php_sprintf(buffer, "string:%s", str->val); 124 | } 125 | 126 | void format_zval_long(char* buffer, zval*z){ 127 | zend_long l= Z_LVAL_P(z); 128 | php_sprintf(buffer, "long:%ld", l); 129 | } 130 | void format_zval_double(char* buffer, zval*z){ 131 | double d= Z_DVAL_P(z); 132 | php_sprintf(buffer, "double:%.10lf", d); 133 | } 134 | void format_zval(char* buffer, zval* z){ 135 | switch(z->u1.v.type){ 136 | case IS_UNDEF: 137 | strcpy(buffer, "undef"); 138 | break; 139 | case IS_NULL: 140 | strcpy(buffer, "null"); 141 | break; 142 | case IS_FALSE: 143 | strcpy(buffer, "false"); 144 | break; 145 | case IS_TRUE: 146 | strcpy(buffer, "true"); 147 | break; 148 | case IS_LONG: 149 | format_zval_long(buffer, z); 150 | break; 151 | case IS_DOUBLE: 152 | format_zval_double(buffer, z); 153 | break; 154 | case IS_STRING: 155 | format_zval_string(buffer, z); 156 | break; 157 | case IS_ARRAY: 158 | strcpy(buffer, "array"); 159 | break; 160 | case IS_OBJECT: 161 | strcpy(buffer, "object"); 162 | break; 163 | case IS_RESOURCE: 164 | strcpy(buffer, "resource"); 165 | break; 166 | case IS_REFERENCE: 167 | strcpy(buffer, "reference"); 168 | break; 169 | default: 170 | strcpy(buffer, "unkown_zval"); 171 | break; 172 | } 173 | } 174 | 175 | void format_znode_op(char* buffer, zend_uchar type, znode_op op, 176 | zend_op_array* op_array) { 177 | zval* z; 178 | switch (type) { 179 | case IS_CONST: 180 | z = RT_CONSTANT_EX(op_array->literals, op); 181 | format_zval(buffer, z); 182 | break; 183 | case IS_TMP_VAR: 184 | php_sprintf(buffer, "TMPVAR+%d", op.jmp_offset); 185 | break; 186 | case IS_VAR: 187 | php_sprintf(buffer, "VAR+%d", op.jmp_offset); 188 | break; 189 | case IS_UNUSED: 190 | strcpy(buffer, "unused"); 191 | break; 192 | case IS_CV: 193 | php_sprintf(buffer, "CV+%d", op.jmp_offset); 194 | break; 195 | default: 196 | strcpy(buffer, "unknown"); 197 | break; 198 | } 199 | } 200 | 201 | void print_opcode(zend_op* op_code, zend_op_array* op_array) { 202 | static char buffer_op1[200]; 203 | static char buffer_op2[200]; 204 | static char buffer_result[200]; 205 | format_znode_op(buffer_op1, op_code->op1_type, op_code->op1, op_array); 206 | format_znode_op(buffer_op2, op_code->op2_type, op_code->op2, op_array); 207 | format_znode_op(buffer_result, op_code->result_type, op_code->result, op_array); 208 | 209 | opdump_printf("%20s\t%s\t%s\t%s\n", zend_get_opcode_name(op_code->opcode), 210 | buffer_op1, buffer_op2, buffer_result); 211 | } 212 | 213 | ZEND_API zend_op_array *opdump_compile_file(zend_file_handle *file_handle, 214 | int type) { 215 | int i; 216 | 217 | char op_filename[500]; 218 | zend_op_array* ret = origin_zend_compile_file(file_handle, type); 219 | strncpy(op_filename, file_handle->filename, sizeof(op_filename)); 220 | strcat(op_filename, ".opcodes"); 221 | 222 | op_file = fopen(op_filename, "w"); 223 | 224 | for (i = 0; i < ret->last; i++) { 225 | print_opcode(ret->opcodes + i, ret); 226 | } 227 | 228 | fclose(op_file); 229 | return ret; 230 | } 231 | 232 | /* Remove if there's nothing to do at request start */ 233 | /* {{{ PHP_RINIT_FUNCTION 234 | */ 235 | PHP_RINIT_FUNCTION(opdump) { 236 | #if defined(COMPILE_DL_OPDUMP) && defined(ZTS) 237 | ZEND_TSRMLS_CACHE_UPDATE(); 238 | #endif 239 | origin_zend_compile_file = zend_compile_file; 240 | zend_compile_file = opdump_compile_file; 241 | return SUCCESS; 242 | } 243 | /* }}} */ 244 | 245 | /* Remove if there's nothing to do at request end */ 246 | /* {{{ PHP_RSHUTDOWN_FUNCTION 247 | */ 248 | PHP_RSHUTDOWN_FUNCTION(opdump) { 249 | zend_compile_file = origin_zend_compile_file; 250 | return SUCCESS; 251 | } 252 | /* }}} */ 253 | 254 | /* {{{ PHP_MINFO_FUNCTION 255 | */ 256 | PHP_MINFO_FUNCTION(opdump) { 257 | php_info_print_table_start(); 258 | php_info_print_table_header(2, "opdump support", "enabled"); 259 | php_info_print_table_end(); 260 | 261 | /* Remove comments if you have entries in php.ini 262 | DISPLAY_INI_ENTRIES(); 263 | */ 264 | } 265 | /* }}} */ 266 | 267 | /* {{{ opdump_functions[] 268 | * 269 | * Every user visible function must have an entry in opdump_functions[]. 270 | */ 271 | const zend_function_entry opdump_functions[] = { 272 | PHP_FE(opdump_test, NULL) /* For testing, remove later. */ 273 | PHP_FE_END /* Must be the last line in opdump_functions[] */ 274 | }; 275 | /* }}} */ 276 | 277 | /* {{{ opdump_module_entry 278 | */ 279 | zend_module_entry opdump_module_entry = { 280 | STANDARD_MODULE_HEADER, "opdump", opdump_functions, 281 | PHP_MINIT(opdump), 282 | PHP_MSHUTDOWN(opdump), 283 | PHP_RINIT(opdump), /* Replace with NULL if there's nothing to do at request start */ 284 | PHP_RSHUTDOWN(opdump), /* Replace with NULL if there's nothing to do at request end */ 285 | PHP_MINFO(opdump), 286 | PHP_OPDUMP_VERSION, 287 | STANDARD_MODULE_PROPERTIES }; 288 | /* }}} */ 289 | 290 | #ifdef COMPILE_DL_OPDUMP 291 | #ifdef ZTS 292 | ZEND_TSRMLS_CACHE_DEFINE(); 293 | #endif 294 | ZEND_GET_MODULE(opdump) 295 | #endif 296 | 297 | /* 298 | * Local variables: 299 | * tab-width: 4 300 | * c-basic-offset: 4 301 | * End: 302 | * vim600: noet sw=4 ts=4 fdm=marker 303 | * vim<600: noet sw=4 ts=4 304 | */ 305 | --------------------------------------------------------------------------------