PHP is to first parse the source code into opcode, and then pass the opcode to ZEND_VM for execution.
//a opcode structure struct _zend_op {const void *handler; //opcode corresponding execution function, each opcode has a corresponding execution function znode_op OP1; //the first element of the execution parameter Znode_op OP2; //the second element of the execution parameter znode_op result; //implementation results uint32_t extended_value; //additional extended fields and values uint32_t Lineno; //line number Zend_uchar opcode; //operation code, the specific operation code list see http://cn.php.net/manual/zh/internals2.opcodes.php Zend_uchar op1_type; //the first element of the type Zend_uchar op2_type; //the type of the second element Zend_uchar Result_type; //result type};
In Php7, we can easily use phpdbg to view a file or a function's opcode. As for the use of phpdbg, there is not much online introduction, but fortunately there is a very detailed help documentation. Here is one of the simplest opcode codes:
$ bin/phpdbg-f/home/xiaoju/software/php7/demo/Echo.phpprompt>List10000001:<?php00002:00003: $a =1; 00004: $b = $a; 00005: $b = $b + 1; 00006: echo $b; print exec[context/home/xiaoju/software/php7/demo/echo.php (6 Ops)]l1-7 {main} ()/home/xiaoju/ Software/php7/demo/echo.php-0x7fe3fae63300 + 6 opsL3 #0 ASSIGN $a 1l4 #1 ASSIGN $b $aL 5 #2 ADD $b 1 ~2l5 #3 ASSIGN $b ~2l6 #4 ECHO $bL 7
#5 RETURN 1
This PHP file does one of the simplest addition operations. 6 _zend_op were generated. Each of the lines shown represents a _zend_op
_zendop.lineno op号 _zend_op.opcode _zend_op.op1 _zend_op.op2 _zend_op.resultL5 #2 ADD $b 1 ~2
Here _zend_op.opcode corresponding operation on the official website have documents and detailed examples can be viewed: http://cn.php.net/manual/zh/internals2.opcodes.php
It is worth saying that phpdbg also has a remote UI version that allows us to access PHP information on the near-end diagnostics server
Gdb
But our goal is to study PHP source code, phpdbg can only analyze to opcode this layer, or is not enough, gdb may be a better choice.
GDB uses the same as usual
For example, I now have a script echo.php:
1 <?php 2 3 $a = 1; 4 $b = $a; 5 $b = $b + 1; 6 echo $b;
My PHP installation path is:
/home/xiaoju/software/php7/bin/php
PHP Source Path in:
/home/xiaoju/webroot/php-src/php-src-master/
Run GDB
/home/xiaoju/software/php7/bin/php
Load Gdbinit:
/home/xiaoju/webroot/php-src/php-src-master/.gdbinit
To set breakpoints:
(gdb) b zend_execute_scripts
Run:
/home/xiaoju/software/php7/demo/echo.php
I want to set a breakpoint on this line in 1459:
1452 for (i = 0; i < file_count; i++) {1453 file_handle = va_arg(files, zend_file_handle *);1454 if (!file_handle) {1455 continue;1456 }14571458 op_array = zend_compile_file(file_handle, type);1459 if (file_handle->opened_path) {1460 zend_hash_add_empty_element(&EG(included_files), file_handle->opened_path);1461 }(gdb) b 1459
Keep running.
(gdb) continue(gdb) s(gdb) s
Print out the op_array of this time
(GDB) P *op_array$4 = {type =2' \002 ', arg_flags ="\000\000", Fn_flags =134217728, function_name =0x0, scope =0x0Prototype =0x0, Num_args =0, Required_num_args =0, Arg_info =0x0, RefCount =0x7ffff6002000,Last =6, opcodes =0x7ffff6076240, Last_var =2, T =4, VARs =0x7ffff6079030, Last_live_range = 0, Last_try_catch = 0, Live_range = 0x0, Try_catch_array = 0x0, stat Ic_variables = 0x0, filename = 0x7ffff605c2d0, Line_start = 1, line_end = 7, doc_comment = 0x0, early_ binding = 4294967295, last_literal = 3, literals = 0x7ffff60030c0, cache_size = 0, Run_time_cache =
0x0, reserved = {
0x0, 0x0, 0x0, 0x0}}
I can optimize the output:
(GDB)Set Print PrettyOn (GDB)P *op_array$5 = {Type =2' \002 ', arg_flags ="\000\000", Fn_flags =134217728, function_name =0x0Scope =0x0, prototype =0x0, Num_args =0, Required_num_args =0, Arg_info =0x0, RefCount =0x7ffff6002000,Last =6, opcodes =0x7ffff6076240, Last_var =2,T =4, VARs =0x7ffff6079030, Last_live_range = 0, Last_try_catch = 0, Live_range = 0x0, Try_catch_array = 0x0, static _variables = 0x0, filename = 0x7ffff605c2d0, Line_start = 1, line_end = 7, doc_comment = 0x0, Early_ binding = 4294967295, last_literal = 3, literals = 0x7ffff60030c0, cache_size = 0, Run_time_cache = 0x0, reserved = {0x0, 0x0, 0x0, 0x0}}
I want to op_array.filename.val the exact value.
40(gdb) p *(op_array.filename.val)@40$13 = "/home/xiaoju/software/php7/demo/echo.php"
Well, we can study the structure of _zend_op_array in the way:
An array of opcode that is compiled to generate this structurestruct _zend_op_array {Zend_uchar type;The type of OP array, such as Zend_eval_code Zend_uchar arg_flags[3];/* Bitset of Arg_info.pass_by_reference */uint32_t fn_flags; Zend_string *function_name; Zend_class_entry *scope; Zend_function *prototype;uint32_t Num_args;Parameters of the scriptuint32_t Required_num_args; Zend_arg_info *arg_info;/* END of common Elements */uint32_t *refcount;The number of references to this structureUint32_t last;The number of opcode zend_op *opcodes;Store all the opcodeint Last_var;Number of PHP variablesuint32_t T; Zend_string **vars;The number of PHP variables that are compiledint last_live_range;int last_try_catch;The number of Try_catch Zend_live_range *live_range; Zend_try_catch_element *try_catch_array;///* static variables support */HashTable *static_variables; Span class= "hljs-comment" >//static variable zend_string *filename; //executed script file uint32_t Line_start; //began in the first few lines uint32_t line_end; //ended in the first few lines zend_string *doc_comment; //Documentation comments uint32_t early_binding; /* the linked list of delayed declarations */int last_literal; Zval *literals; int cache_size; void **run_time_cache; void *reserved[zend_max_reserved_resources]; //reserved field};
Go from http://www.cnblogs.com/yjf512/
PHP Kernel parsing