使用PHP Embed SAPI实现Opcodes查看器

使用PHP Embed SAPI实现Opcodes查看器

http://www.laruence.com/2008/09/23/539.html

 

PHP提供了一个Embed SAPI,也就是说,PHP容许你在C/C++语言中调用PHP/ZE提供的函数。本文就通过基于Embed SAPI实现一个PHP的opcodes查看器。
首先,下载PHP源码以供编译, 我现在使用的是PHP5.3 alpha2
进入源码目录:

       
       
       
       
  1. ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql --with-config-file-path=/etc/
  2. ./make
  3. ./make instal

最后,记得要将生成的libphp5.so复制到运行时库的目录,我直接拷贝到了/lib/, 否则会在运行你自己的embed程序的时候报错:

       
       
       
       
  1. ./embed: error while loading shared libraries: libphp5.so: cannot open shared object file: No such file or director

如果你对PHP的SAPI还不熟悉的话,我建议你看看我的这篇文章:深入理解Zend SAPIs(Zend SAPI Internals)
这个时候,你就可以在你的C代码中,嵌入PHP脚本解析器了, 我的例子:

       
       
       
       
  1. #include "sapi/embed/php_embed.h"
  2.  
  3. int main(int argc, char * argv[]){
  4.     PHP_EMBED_START_BLOCK(argc,argv);
  5.     char * script = " print 'Hello World!';";
  6.     zend_eval_string(script, NULL,
  7.                                       "Simple Hello World App" TSRMLS_CC);
  8.     PHP_EMBED_END_BLOCK();
  9.     return 0;
  10. }
  11.  

然后就是要指明include path了,一个简单的Makefile

       
       
       
       
  1. CC = gcc
  2. CFLAGS = -I/usr/local/include/php/ \
  3.             -I/usr/local/include/php/main \
  4.             -I/usr/local/include/php/Zend \
  5.             -I/usr/local/include/php/TSRM \
  6.             -Wall -g
  7. LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5
  8. ALL:
  9.     $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS

编译成功以后, 运行,我们可以看到, stdout输出 Hello World!

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:
首先我们定义opcode的转换函数(全部的opcodes可以查看Zend/zend_vm_opcodes.h);

       
       
       
       
  1. char *opname(zend_uchar opcode){
  2.     switch(opcode) {
  3.         case ZEND_NOP: return "ZEND_NOP"; break;
  4.         case ZEND_ADD: return "ZEND_ADD"; break;
  5.         case ZEND_SUB: return "ZEND_SUB"; break;
  6.         case ZEND_MUL: return "ZEND_MUL"; break;
  7.         case ZEND_DIV: return "ZEND_DIV"; break;
  8.         case ZEND_MOD: return "ZEND_MOD"; break;
  9.         case ZEND_SL: return "ZEND_SL"; break;
  10.         case ZEND_SR: return "ZEND_SR"; break;
  11.         case ZEND_CONCAT: return "ZEND_CONCAT"; break;
  12.         case ZEND_BW_OR: return "ZEND_BW_OR"; break;
  13.         case ZEND_BW_AND: return "ZEND_BW_AND"; break;
  14.         case ZEND_BW_XOR: return "ZEND_BW_XOR"; break;
  15.         case ZEND_BW_NOT: return "ZEND_BW_NOT"; break;
  16.         /*...省略 ....*/
  17.         default : return "UNKNOW"; break;

然后定义zval和znode的输出函数:

       
       
       
       
  1.  char *format_zval(zval *z)
  2. {
  3.     static char buffer[BUFFER_LEN];
  4.     int len;
  5.  
  6.     switch(z->type) {
  7.         case IS_NULL:
  8.             return "NULL";
  9.         case IS_LONG:
  10.         case IS_BOOL:
  11.             snprintf(buffer, BUFFER_LEN, "%d", z->value.lval);
  12.             return buffer;
  13.         case IS_DOUBLE:
  14.             snprintf(buffer, BUFFER_LEN, "%f", z->value.dval);
  15.             return buffer;
  16.         case IS_STRING:
  17.             snprintf(buffer, BUFFER_LEN, "\"%s\"", z->value.str.val);
  18.             return buffer;
  19.         case IS_ARRAY:
  20.         case IS_OBJECT:
  21.         case IS_RESOURCE:
  22.         case IS_CONSTANT:
  23.         case IS_CONSTANT_ARRAY:
  24.             return "";
  25.         default:
  26.             return "unknown";
  27.     }
  28. }
  29.  
  30. char * format_znode(znode *n){
  31.     static char buffer[BUFFER_LEN];
  32.  
  33.     switch (n->op_type) {
  34.         case IS_CONST:
  35.             return format_zval(&n->u.constant);
  36.             break;
  37.         case IS_VAR:
  38.             snprintf(buffer, BUFFER_LEN, "$%d", n->u.var/sizeof(temp_variable));
  39.             return buffer;
  40.             break;
  41.         case IS_TMP_VAR:
  42.             snprintf(buffer, BUFFER_LEN, "~%d", n->u.var/sizeof(temp_variable));
  43.             return buffer;
  44.             break;
  45.         default:
  46.             return "";
  47.             break;
  48.     }
  49. }
  50.  

然后定义op_array的输出函数:

       
       
       
       
  1. void dump_op(zend_op *op, int num){
  2.     printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno,
  3.             opname(op->opcode),
  4.             format_znode(&op->op1),
  5.             format_znode(&op->op2),
  6.             format_znode(&op->result)) ;
  7. }
  8.  
  9. void dump_op_array(zend_op_array *op_array){
  10.     if(op_array) {
  11.         int i;
  12.         printf("%5s %5s %30s %040s %040s %040s\n", "opnum", "line", "opcode", "op1", "op2", "result");
  13.         for(i = 0; i < op_array->last; i++) {
  14.             dump_op(&op_array->opcodes[i], i);
  15.         }
  16.     }
  17. }

最后,就是程序的主函数了:

       
       
       
       
  1. int main(int argc, char **argv){
  2.     zend_op_array *op_array;
  3.     zend_file_handle file_handle;
  4.  
  5.     if(argc != 2) {
  6.         printf("usage: op_dumper <script>\n");
  7.         return 1;
  8.     }
  9.     PHP_EMBED_START_BLOCK(argc,argv);
  10.     printf("Script: %s\n", argv[1]);
  11.     file_handle.filename = argv[1];
  12.     file_handle.free_filename = 0;
  13.     file_handle.type = ZEND_HANDLE_FILENAME;
  14.     file_handle.opened_path = NULL;
  15.     op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
  16.     if(!op_array) {
  17.         printf("Error parsing script: %s\n", file_handle.filename);
  18.         return 1;
  19.     }
  20.     dump_op_array(op_array);
  21.     PHP_EMBED_END_BLOCK();
  22.     return 0;
  23. }

编译,运行测试脚本(sample.php):
sample.php:

       
       
       
       
  1.    echo "laruence";

命令:

       
       
       
       
  1. ./opcodes_dumper sample.ph

得到输出结果(如果你对下面的结果很迷惑,那么建议你再看看我的这篇文章:深入理解PHP原理之Opcodes):

       
       
       
       
  1. Script: sample.php
  2. opnum line opcode op1 op2 result
  3.     0 2 ZEND_ECHO "laruence"
  4.     1 4 ZEND_RETURN

呵呵,怎么样,是不是很好玩呢?
源码地址:http://code.google.com/p/opcodesdumper/

你可能感兴趣的:(使用PHP Embed SAPI实现Opcodes查看器)