PHP HOOK OPCode 是通过
int zend_set_user_opcode_handler(zend_uchar opcode, opcode_handler_t handler)
接口替换了 PHP 内置的 OPCode 的 handler (函数指针),那 PHP 是如何执行到我们替换后的 handler 的呢?
一、zend_user_opcodes 和 zend_user_opcode_handlers
以 PHP 5.1 版本为例,zend_set_user_opcode_handler() 接口的实现如下:
ZEND_API int zend_set_user_opcode_handler(zend_uchar opcode, opcode_handler_t handler)
{
??? if (opcode != ZEND_USER_OPCODE) {
??????? zend_user_opcodes[opcode] = ZEND_USER_OPCODE;
??????? zend_user_opcode_handlers[opcode] = handler;
??????? return SUCCESS;
??? }
??? return FAILURE;
}
这里面用到了两个静态数组 zend_user_opcodes 和 zend_user_opcode_handlers,这两个数组的声明和初始化是由 zend_vm_gen.php 脚本根据 zend_vm_def.h 生成的,具体生成过程可参考
PHP内核生成zend_vm_opcodes.h - 知乎 (zhihu.com)
生成的 zend_cm_execute.h 文件里有这两个数组的声明和初始化,如下:
static opcode_handler_t zend_user_opcode_handlers[256] = {(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL,(opcode_handler_t)NULL};
?
static zend_uchar zend_user_opcodes[256] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
两个数组的下标代表 opcode 的值,当我们调用 zend_set_user_opcode_handler 时,除了会改变该 opcode 对应的 zend_user_opcode_handlers 的值,对应 opcode 的 zend_user_opcodes 的数组值会全部变成150(ZEND_USER_OPCODE 对应的值)。
二、zend_opcode_handlers
由 zend_vm_gen.php 生成的 zend_vm_execute.h 文件里还有个 zend_opcode_handlers 数组,这个数组的初始化如下:
void zend_init_opcodes_handlers()
{
? static const opcode_handler_t labels[] = {
??? 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,
??? ZEND_NULL_HANDLER,
??? ZEND_ADD_SPEC_CONST_CV_HANDLER,
??? ZEND_ADD_SPEC_TMP_CONST_HANDLER,
??? ZEND_ADD_SPEC_TMP_TMP_HANDLER,
??? ZEND_ADD_SPEC_TMP_VAR_HANDLER,
??? ZEND_NULL_HANDLER,
??? ZEND_ADD_SPEC_TMP_CV_HANDLER,
??? ZEND_ADD_SPEC_VAR_CONST_HANDLER,
??? ZEND_ADD_SPEC_VAR_TMP_HANDLER,
??? ...
};
??? zend_opcode_handlers = (opcode_handler_t*)labels;
}
zend_opcode_handlers 包含有 3776 个 handlers,这个数字时怎么来的呢?
PHP 的 handler 是每条 opcode 对应的 C 语言编写的处理逻辑,我们可以在每个 opcode 对应一个 handler,在一个 handler 里对两个操作数的所有类型进行处理(PHP 5.0 的处理方式),也可以根据两个操作数的类型,直接定义不同的 handler 函数,而 PHP 5.0 以上的版本采取的就是后面的方式,以 PHP 5.1 为例,每个操作数有 5 种类型,两个操作数两两组合,就有 25 种可能,所以每个 opcode 对应 25 个handler,每个handler的命令方式就变成了{OPCode_name}_SPEC_{OP1_TYPE}_{OP2_TYPE}, 一共 151 个 opcode,对应 151 * 25 = 3775 个 handler,最后数组加了一个空 handler,所以一共 3776 个 handlers。
三、zend_opcode_handlers 和 zend_user_opcode_handlers 映射关系
zend_opcode_handlers 里一已经包含了所有 opcode 的 handler,但是怎么找到 opcode 对应的 handler 呢?
这部分代码如下:
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?? ??*/
??????? };
??????? return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
}
?
ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
??? op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}
?
加入我们调用 zend_set_user_opcode_handler(ZEND_ADD, handler),对 ZEND_ADD(对应的值为 1),则 zend_user_opcodes[1] = 150,这个 opcode 的 handler 会通过 zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op); 设置,此时相当于调用 zend_vm_get_opcode_handler(150, op);
而zend_vm_get_opcode_handler里的zend_vm_decode数组代表操作数的类型,只有五种类型生效,对应的值也是0~ 4,最后通过opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]
这个下标即可找到 opcode 对应的 handler, 假如两个操作数类型都为 const,可计算出下标值为 150 * 25 + 0 * 5 + 0 = 3750,对应的 handler 为 ZEND_USER_OPCODE_SPEC_HANDLER,其实?? ZEND_USER_OPCODE 这个 opcode 的25 个handler 都是 ZEND_USER_OPCODE_SPEC_HANDLER,具体实现为:
static int ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{??
????int ret = zend_user_opcode_handlers[EX(opline)->opcode](ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL);
?
??? switch (ret) {
??????? case ZEND_USER_OPCODE_CONTINUE:
??????????? ZEND_VM_CONTINUE();
??????? case ZEND_USER_OPCODE_RETURN:
??????????? ZEND_VM_RETURN();
??????? case ZEND_USER_OPCODE_DISPATCH:
??????????? ZEND_VM_DISPATCH(EX(opline)->opcode, EX(opline));
??????? default:
??????????? ZEND_VM_DISPATCH(ret & 0xff, EX(opline));
??? }
}
可以看到里面执行了该 opcode 对应的 handler,也就是我们用 zend_set_user_handler 设置的 handler。
|