Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2418309
  • 博文数量: 392
  • 博客积分: 7040
  • 博客等级: 少将
  • 技术积分: 4138
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-17 13:03
个人简介

范德萨发而为

文章分类

全部博文(392)

文章存档

2017年(5)

2016年(19)

2015年(34)

2014年(14)

2013年(47)

2012年(40)

2011年(51)

2010年(137)

2009年(45)

分类: Python/Ruby

2011-05-25 11:08:29

PHP源代码分析-zend最终调用函数分析
原文出处:


在追踪zend 内核的时候发现最终调用的函数都在zend_vm_execute.h文件里面定义了,也就是说基本每一个PHP的语句,比如定义一个函数、echo一个表达式等任何一个语句最后的实现都是在zend_vm_execute.h里面定义了,
在zend.c文件zend_startup()函数里面调用了zend_init_opcodes_handlers(),初始化了labels数组,这个数组的类型是opcode_handler_t,这个类型在zend_compile.h文件里面定义了:
typedef int (*opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
//在zend_vm_execute.h引用zend_opcode_handlers,

//zend_opcode_handlers = (opcode_handler_t*)labels;

extern ZEND_API opcode_handler_t *zend_opcode_handlers;


opcode_handler_t是函数指针,指定一个用来处理每一个opcode的函数的入口地址,这些地址都保存在labels数组里面了。

static const opcode_handler_t labels[] = {
        ZEND_NOP_SPEC_HANDLER,
      ZEND_NOP_SPEC_HANDLER,
        //中间省略。。。。。

        ZEND_NOP_SPEC_HANDLER,
      ZEND_ADD_SPEC_CONST_CONST_HANDLER,
      ZEND_ADD_SPEC_CONST_TMP_HANDLER,
      ZEND_ADD_SPEC_CONST_VAR_HANDLER,
        。。。。。。。
}


这个结构体包含了近3776个成员,这些成员都是函数名称,每次执行一个opcode的时候都要到这个labels里面找对应的handler处理函数。
在zend_vm_execute.h文件最后的有两个函数:

static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)
{
        static const int zend_vm_decode[] = {
            _UNUSED_CODE, /* 0              */
            _CONST_CODE,  /* 1 = IS_CONST   */
            _TMP_CODE,    /* 2 = IS_TMP_VAR */
            _UNUSED_CODE, /* 3              */
            _VAR_CODE,    /* 4 = IS_VAR     */
            _UNUSED_CODE, /* 5              */
            _UNUSED_CODE, /* 6              */
            _UNUSED_CODE, /* 7              */
            _UNUSED_CODE, /* 8 = IS_UNUSED  */
            _UNUSED_CODE, /* 9              */
            _UNUSED_CODE, /* 10             */
            _UNUSED_CODE, /* 11             */
            _UNUSED_CODE, /* 12             */
            _UNUSED_CODE, /* 13             */
            _UNUSED_CODE, /* 14             */
            _UNUSED_CODE, /* 15             */
            _CV_CODE      /* 16 = IS_CV     */
        };
        //这句很关键,就是返回处理当前opcode的handler,

                //zend_opcode_handlers等于上面定义的lables

        return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
}

//设置zend_op里面的handler,指定处理的函数

ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
    op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}


如果想知道每个opcode是交给哪个handler处理,可以在
return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
这条语句前面打印出索引值:

int index;
index = opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type];
printf("zend_opcode_handlers_index:%d\n", index);


然后再重新 make && make install
编写一个test.php文件:
$a = "test";
?>
再运行/usr/local/php5.2.9/bin/php  test.php
输出的结果如下:
zend_opcode_handlers_index:970
zend_opcode_handlers_index:1553
zend_opcode_handlers_index:3743
简单的一个赋值语句,最后执行了三个函数,这三个数字代表在labels里面的偏移量,找到labels里面第970个值:
ZEND_ASSIGN_SPEC_CV_CONST_HANDLER
再找到这个函数:

static int ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = EX(opline);

    zval *value = &opline->op2.u.constant;

     zend_assign_to_variable(&opline->result, &opline->op1, &opline->op2, value, (0?IS_TMP_VAR:IS_CONST), EX(Ts) TSRMLS_CC);
    /* zend_assign_to_variable() always takes care of op2, never free it! */

    ZEND_VM_NEXT_OPCODE();
}


这个函数主要是对变量进行赋值,把值存进opline->result里面。后面1553和3743偏移值分别是
ZEND_RETURN_SPEC_CONST_HANDLER
ZEND_HANDLE_EXCEPTION_SPEC_HANDLER
分别是用来处理函数返回和异常,每个PHP文件执行到最后都会自动执行这两个函数。

另外想要快速查找每个偏移量对应的是哪个函数,可以先把  static const opcode_handler_t labels[] 里面的3776个成员保存在一个文件opcode_handler.txt,然后写个函数来智能处理:


#include<stdio.h>
#include<stdlib.h>
int main(int c, char *v[]){
   //在labels的偏移量

    int offset = atoi(v[1]);
    FILE *fp;
    fp = fopen("/home/dexin/op_code_handler.txt","r");
    char handler[200];
    int i = 0;
    while(!feof(fp)){
        fgets(handler,1024,fp);
        if(i != offset){
            i++;
        }else{
            printf("opcode handler:%s\n", handler);
            break;
        }
    
    }
    return 1;
}


编译:gcc -g -o gethandler gethandler.c
然后把gethandler 放入/bin/gethandler就可以了,比如我想查询偏移量为970的handler:
gethandler 970
结果:opcode handler:         ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,
再去找到这个函数的实现就好了。
写完之后发现用shell写更加快:
#gethandler_awk
#!/bin/bash
awk 'NR == offset {print "opcode handler:"$0} offset=$1 /home/dexin/op_code_handler.txt

chmod+x gethandler_awk
放入/bin/gethandler_awk
gethandler_awk 970
同样可以得出结果
opcode handler:         ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,

%3D2%26amp%3Bfilter%3Ddigest
阅读(1474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~