使用PHP Embed SAPI实现Opcodes查看器 | 风雪之隅

23 Sep 08 使用PHP Embed SAPI实现Opcodes查看器

0

作者: Laruence( )

本文地址: http://ift.tt/1Bg69rA

转载请注明出处

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

 ./configure –enable-embed –with-config-file-scan-dir=/etc/php.d –with-mysql –with-config-file-path=/etc/

 ./make

 ./make install

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

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

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

#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;

}

 

然后就是要指明include path了,一个简单的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)

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

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:
首先我们定义opcode的转换函数(全部的opcodes可以查看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;

然后定义zval和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->http://ift.tt/1DCHq4K);

            return buffer;

            break;

        case IS_TMP_VAR:

            snprintf(buffer, BUFFER_LEN, “~%d”, n->http://ift.tt/1DCHq4K);

            return buffer;

            break;

        default:

            return “”;

            break;

    }

}

 

然后定义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);

        }

    }

}

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

int main(int argc, char **argv){

    zend_op_array *op_array;

    zend_file_handle file_handle;

 

    if(argc != 2) {

        printf(“usage: op_dumper