How to implement the Opcodes Viewer using PHP Embed SAPI, Sapiopcodes
PHP provides a embed SAPI, which means that PHP allows you to invoke functions provided by php/ze in the C + + language. This article implements a PHP Opcodes viewer based on the embed SAPI.
First of all, download the PHP source code for compiling, I now use PHP5.3 alpha2
Enter the source directory:
./configure--enable-embed--WITH-CONFIG-FILE-SCAN-DIR=/ETC/PHP.D--with-mysql--with-config-file-path=/etc/
./make
./make Install
Finally, remember to copy the generated libphp5.so to the runtime library directory, I copied directly to the/lib/, otherwise you will run your own embed program when the error:
./embed:error while loading shared libraries:libphp5.so:cannot open Shared object file:no such file or directory
If you are not familiar with PHP SAPI, I suggest you look at my article: in-depth understanding Zend Sapis (Zend SAPI Internals)
This time, you can embed the PHP script parser in your C code, my example:
#include "sapi/embed/php_embed.h" int main (int argc, char * argv[]) {php_embed_start_block (ARGC,ARGV); char * script = "PR int ' Hello world! '; "; zend_eval_string (script, NULL, "Simple Hello World App" TSRMLS_CC); Php_embed_end_block (); return 0;}
Then it is to indicate the include path, a simple makefile
CC = Gcccflags =-i/usr/local/include/php/\ -i/usr/local/include/php/main \ -i/usr/local/include/php/zend \ -i/usr/local/include/php/tsrm \ -wall-gldflags =-lstdc++-l/usr/local/lib-lphp5all: $ (CC)-O embed Embed.cpp $ (CFLAGS) $ (ldflags)
After the compilation succeeds, run, we can see that the stdout output Hello world!
Based on this, we can easily implement a VLD-like opcodes dumper:
First we define the opcode conversion function (all opcodes can see the zend/zend_vm_opcodes.h);
Char *opname (Zend_uchar opcode) {switch (opcode) {case Zend_nop:return "Zend_nop"; Case Zend_add:return "Zend_add"; break; Case Zend_sub:return "Zend_sub"; break; Case Zend_mul:return "Zend_mul"; break; Case Zend_div:return "Zend_div"; break; Case Zend_mod:return "Zend_mod"; break; Case Zend_sl:return "ZEND_SL"; break; Case Zend_sr:return "ZEND_SR"; break; Case Zend_concat:return "Zend_concat"; break; Case Zend_bw_or:return "Zend_bw_or"; break; Case Zend_bw_and:return "Zend_bw_and"; break; Case Zend_bw_xor:return "Zend_bw_xor"; break; Case Zend_bw_not:return "Zend_bw_not"; break; /*... Omit .... */ Default:return "unknow"; break;
Then define the output functions for Zval and Znode:
Char *format_zval (zval *z) {static char buffer[buffer_len]; int LEN; switch (z->type) {case Is_null:return "NULL"; Case Is_long:case is_bool:snprintf (buffer, Buffer_len, "%d", z->value.lval); return buffer; Case is_double:snprintf (buffer, Buffer_len, "%f", z->value.dval); return buffer; Case is_string:snprintf (buffer, Buffer_len, "\"%s\ "", z->value.str.val); return buffer; Case Is_array:case is_object:case is_resource:case is_constant:case is_constant_array:return ""; Default:return "Unknown"; }}char * Format_znode (Znode *n) {static char Buffer[buffer_len]; switch (n->op_type) {case Is_const:return Format_ Zval (&n->u.constant); Break Case is_var:snprintf (buffer, Buffer_len, "$%d", N->u.var/sizeof (temp_variable)); return buffer; Break Case is_tmp_var:snprintf (buffer, Buffer_len, "~%d", N->u.var/sizeof (temp_variable)); return buffer; Break Default:return ""; Break }}
Then define the output function of the Op_array:
void Dump_op (zend_op *op, int num) {printf ("%5d%5d%30s%040s%040s%040s\n", num, Op->lineno, Opname (op->opcod e), Format_znode (&OP->OP1), Format_znode (&OP->OP2), Format_znode (&op->result) ) ;} void Dump_op_array (Zend_op_array *op_array) {if (Op_array) { int i; printf ("%5s%5s%30s%040s%040s%040s\n", "Opnum", "line", "opcode", "OP1", "OP2", "result"); for (i = 0; i < op_array->last; i++) { dump_op (&op_array->opcodes[i], i);}}}
Finally, the main function of the program is:
int main (int argc, char **argv) {Zend_op_array *op_array; zend_file_handle file_handle; if (argc! = 2) {printf ("Usage:op _dumper