Home  >  Article  >  Backend Development  >  How to implement Opcodes viewer using PHP Embed SAPI, sapiopcodes_PHP tutorial

How to implement Opcodes viewer using PHP Embed SAPI, sapiopcodes_PHP tutorial

WBOY
WBOYOriginal
2016-07-12 09:05:15802browse

How to use PHP Embed SAPI to implement Opcodes viewer, sapiopcodes

PHP provides an Embed SAPI, that is to say, PHP allows you to call PHP/ZE in C/C language provided function. This article implements a PHP opcodes viewer based on Embed SAPI.

First, download the PHP source code for compilation. I am currently using PHP5.3 alpha2

Enter the source code 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 directory of the runtime library. I copied it directly to /lib/, otherwise an error will be reported when running your own embed program:

./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’s SAPI, I suggest you read this article of mine: In-depth understanding of Zend SAPIs (Zend SAPI Internals)
At 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 = " print 'Hello World!';";
 zend_eval_string(script, NULL,
          "Simple Hello World App" TSRMLS_CC);
 PHP_EMBED_END_BLOCK();
 return 0;
}

Then you need to specify the include path, a simple Makefile

CC = gcc
CFLAGS = -I/usr/local/include/php/ \
   -I/usr/local/include/php/main \
   -I/usr/local/include/php/Zend \
   -I/usr/local/include/php/TSRM \
   -Wall -g
LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5
ALL:
 $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS)

After successful compilation and running, we can see that stdout outputs Hello World!

Based on this, we can easily implement an Opcodes dumper similar to vld:
First we define the opcode conversion function (all opcodes can be viewed in Zend/zend_vm_opcodes.h);

char *opname(zend_uchar opcode){
 switch(opcode) {
  case ZEND_NOP: return "ZEND_NOP"; break;
  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;
  /*...省略 ....*/
  default : return "UNKNOW"; break;

Then define the output functions of 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 op_array:

void dump_op(zend_op *op, int num){
 printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno,
   opname(op->opcode),
   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, there is the main function of the program:

int main(int argc, char **argv){
 zend_op_array *op_array;
 zend_file_handle file_handle;
 if(argc != 2) {
  printf("usage: op_dumper <script>\n");
  return 1;
 }
 PHP_EMBED_START_BLOCK(argc,argv);
 printf("Script: %s\n", argv[1]);
 file_handle.filename = argv[1];
 file_handle.free_filename = 0;
 file_handle.type = ZEND_HANDLE_FILENAME;
 file_handle.opened_path = NULL;
 op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC);
 if(!op_array) {
  printf("Error parsing script: %s\n", file_handle.filename);
  return 1;
 }
 dump_op_array(op_array);
 PHP_EMBED_END_BLOCK();
 return 0;
}

Compile and run the test script (sample.php):

Copy code The code is as follows:
sample.php:
echo "laruence";

Command:

Copy code The code is as follows:
./opcodes_dumper sample.php

Get the output result (if you are confused by the results below, I suggest you read my article: In-depth understanding of PHP principles: Opcodes):

Script: sample.php

opnum line opcode op1 op2 result
0 2 2 ZEND_ECHO "laruence"
1 4 1 ZEND_RETURN 1

Haha, how about it? Isn’t it fun?

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/1069344.htmlTechArticleHow to use PHP Embed SAPI to implement Opcodes viewer, sapiopcodes PHP provides an Embed SAPI, that is, PHP allows You call functions provided by PHP/ZE in C/C language. This article is passed...
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn