php脚本解析与调用
前言:
为了更好的开发php扩展,提高扩展的稳定性与效率,了解zend的内存管理是必不可少的,那么如何切入呢?我这里想到的是分析简单的php脚本(而不是直接分析其内存管理代码,因为多半会导致不知道怎么使用),然后查看脚本中的数据在内核中是怎么储存的,进而更好的了解内存管理.这边文章就从编译过程入手.
目录:
- 赋值语句的分析
- 查看脚本OP ARRAY
- 分析变量引用计数
赋值语句的分析
我们将分析一个简单的脚本来看看php在编译这个脚本的时候, 在内核中都做了什么? 能力有限,只能从执行opcode开始了.
test.php(文件就3行)
<?php
$str = 'aaa';
$strCopy = $str;
然后我们在gdb中通过调试php来查看内核是怎么分析这个脚本的。
#gdb /root/php7d/bin/php
(gdb) set args /home/yaoguai/github/test.php
//我们知道php是在zend_execute函数中调用execute_ex函数执行opcode.
(gdb) b execute_ex
(gdb) r
然后程序停止在
if (UNEXPECTED((ret = OPLINE->handler(execute_data)) != 0)) {
if (EXPECTED(ret > 0)) {
execute_data = EG(current_execute_data);
} else {
return;
}
}
OPLINE->handler即是调用的函数指针,展开为execute_data->opline->handler
#define EX(element) ((execute_data)->element)
#define OPLINE EX(opline)
//打印该函数指针
(gdb) p execute_data->opline->handler
$1 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
从前面的文章中我们知道非函数/方法内的一般变量是保存在executor_globals.symbol_table变量中的,现在我们通过gdb打印这个变量.
(gdb) p executor_globals.symbol_table
$6 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000',
gc_info = 0}, type_info = 7}}, u = {v = {flags = 10 '\n',
nApplyCount = 0 '\000', nIteratorsCount = 0 '\000', reserve = 0 '\000'},
flags = 10}, nTableSize = 64, nTableMask = 63, nNumUsed = 9,
nNumOfElements = 9, nInternalPointer = 0, nNextFreeElement = 0,
arData = 0x7ffff685a000, arHash = 0x7ffff685a800,
pDestructor = 0x81fe95 <_zval_ptr_dtor_wrapper>}
可以知道符号表中有9个元素,前面的文章中我们定义了一个打印hash_table的gdb函数.
(gdb) print_hash executor_globals.symbol_table
_GET IS_ARRAY
_POST IS_ARRAY
_COOKIE IS_ARRAY
_FILES IS_ARRAY
argv IS_ARRAY
argc IS_LONG
_SERVER IS_ARRAY
str 15
strCopy 15
这里的15是IS_INDIRECT,意思很明显是"直接的"的意思.
#define IS_INDIRECT 15
现在我们使用gdb查看一下这个$str变量中内容是什么.
(gdb) printf "%s",executor_globals.symbol_table.arData[7].key.val
str
(gdb) call php_var_dump(executor_globals.symbol_table.arData[7].val,1)
UNKNOWN:0
可以看到,变量中没有保存任何东西.从而也知道了IS_INDIRECT代表的意思.
我们现在看看ZEND_ASSIGN_SPEC_CV_CONST_HANDLER这个函数的实现,首先分析函数的名字, CV是compile_var的意思,const是常量的意思.结合php脚本语句可以知道是把常量赋值给编译变量的意思.
(gdb) s
ZEND_ASSIGN_SPEC_CV_CONST_HANDLER (execute_data=0x7ffff6815030)
//省略其他的代码
value = EX_CONSTANT(opline->op2);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
(gdb) p *value
$12 = {value = {lval = 140737328986688, dval = 6.9533479339779995e-310,
counted = 0x7ffff6803a40, str = 0x7ffff6803a40, arr = 0x7ffff6803a40,
obj = 0x7ffff6803a40, res = 0x7ffff6803a40, ref = 0x7ffff6803a40,
ast = 0x7ffff6803a40, zv = 0x7ffff6803a40, ptr = 0x7ffff6803a40,
ce = 0x7ffff6803a40, func = 0x7ffff6803a40, ww = {w1 = 4135598656,
w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
//可以看到value.u1.type=6即是IS_STRING的意思
(gdb) printf "%s",value.value.str.val
aaa
(gdb) call php_var_dump(executor_globals.symbol_table.arData[7].val,1)
UNKNOWN:0
//这时$str依旧没有被赋值
value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
(gdb) printf "%s",value.value.str.val
aaa
(gdb) p value
$15 = (zval *) 0x7ffff6815090
//可以看到value的值并没有发生变化
(gdb) call php_var_dump(executor_globals.symbol_table.arData[7].val,1)
string(3) "aaa"
//然后再打印$str的值,发现已经变成"aaa",可以得知赋值的操作是zend_assign_to_variable执行的.
(gdb) call php_var_dump(executor_globals.symbol_table.arData[8].val,1)
UNKNOWN:0
//也可以发现$strCopy的值还是空的
//if条件不成立,调用ZEND_VM_NEXT_OPCODE();继续分析下一个OPCODE
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
到这里$str = 'aaa';这句代码就分析完了. 下面分析$str = $strCopy;这句.
(gdb) n
(gdb) p execute_data->opline->handler
$1 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
//这里可以猜测函数的意思是编译变量赋值给编译变量的意思
(gdb) s
ZEND_ASSIGN_SPEC_CV_CV_HANDLER (execute_data=0x7ffff6815030)
value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
(gdb) call php_var_dump(executor_globals.symbol_table.arData[7].val,1)
string(3) "aaa"
(gdb) call php_var_dump(executor_globals.symbol_table.arData[8].val,1)
UNKNOWN:0
(gdb) call php_var_dump(value,1)
string(3) "aaa"
(gdb) call php_var_dump(variable_ptr,1)
string(3) "aaa"
value = zend_assign_to_variable(variable_ptr, value, IS_CV);
(gdb) call php_var_dump(executor_globals.symbol_table.arData[8].val,1)
string(3) "aaa"
//这里看到执行zend_assign_to_variable后$strCopy的值也变成"aaa"了.
(gdb) p &executor_globals.symbol_table.arData[8].val.value.str.val
$8 = (char (*)[1]) 0x7ffff6803a58
(gdb) p &executor_globals.symbol_table.arData[7].val.value.str.val
$9 = (char (*)[1]) 0x7ffff6803a58
(gdb) p &executor_globals.symbol_table.arData[7].val
$10 = (zval *) 0x7ffff685a0e0
(gdb) p &executor_globals.symbol_table.arData[8].val
$11 = (zval *) 0x7ffff685a100
//这里我们可以看到两个zval地址不同,但是存储的string "aaa"地址相同.
继续执行程序.
(gdb) p execute_data->opline->handler
$4 = (opcode_handler_t) 0x87d09b <ZEND_RETURN_SPEC_CONST_HANDLER>
//从前面的分析我们知道$str,$strCopy已经被正确赋值了,那么这是在干什么呢?
(gdb) s
ZEND_RETURN_SPEC_CONST_HANDLER (execute_data=0x7ffff6815030)
(gdb) s
zend_leave_helper_SPEC (execute_data=0x7ffff6815030)
//最后进入到这个条件分支
else /* if (call_kind == ZEND_CALL_TOP_CODE) */ {
zend_array *symbol_table = EX(symbol_table);
zend_detach_symbol_table(execute_data);
old_execute_data = EX(prev_execute_data);
while (old_execute_data) {
if (old_execute_data->func && ZEND_USER_CODE(old_execute_data->func->op_array.type)) {
if (old_execute_data->symbol_table == symbol_table) {
zend_attach_symbol_table(old_execute_data);
}
break;
}
old_execute_data = old_execute_data->prev_execute_data;
}
EG(current_execute_data) = EX(prev_execute_data);
}
zend_vm_stack_free_call_frame(execute_data);
ZEND_VM_RETURN();//return -1;
最后还是返回execute_ex函数中,此时函数的返回值是-1
if (UNEXPECTED((ret = OPLINE->handler(execute_data)) != 0)) {
if (EXPECTED(ret > 0)) {
execute_data = EG(current_execute_data);
} else {
return;
}
}
//根据条件会执行return;语句,直接跳出了while循环.
(gdb) n
zend_execute_scripts (type=8, retval=0x0, file_count=3)
最后返回zend_execute_scripts函数.然后执行完我们的脚本,再调佣rshutdown函数,最后mshutdown,至此php程序正常终止.
查看脚本 OP ARRAY
在上面的赋值语句中,我们总共得到了三个回调函数指针,下面我们通过打印op_array->opcodes字段来查看所有的回调. 首先我们定义一个gdb的函数.
define get_op_handlers
set $i = 0
while $arg0[$i]
p $arg0[$i].handler
set $i = $i + 1
end
end
# gdb /root/php7d/bin/php
(gdb) set args /home/yaoguai/github/test.php
(gdb) b zend_execute
(gdb) r
(gdb) get_op_handlers op_array->opcodes
$3 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
$4 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
$5 = (opcode_handler_t) 0x87d09b <ZEND_RETURN_SPEC_CONST_HANDLER>
$6 = (opcode_handler_t) 0x60
$7 = (opcode_handler_t) 0x7ffff6873180
//执行到ZEND_RETURN_SPEC_CONST_HANDLER后,程序就进入了退出流程了.
(余下部分参考 http://www.nowamagic.net/librarys/veda/detail/1325 http://www.php-internals.com/book/?p=chapt07/07-00-zend-vm)
分析变量引用计数
下面我们分析一下变量的引用技术,与变量间的赋值情况.因此我们写了一个测试文件var.php
var.php
<?php
$aStr = "aaa";
$aStrCopy = $aStr;
$aStrCopy2 = $aStr;
$bStr = "bbb";
$bStrRef = &$bStr;
unset($aStr);
unset($bStrRef);
首先我们的zend_execute停住,使用自定义的get_op_handlers打印出所有的回调函数.
(gdb) set args /home/yaoguai/github/var.php
(gdb) b zend_execute
(gdb) r
(gdb) get_op_handlers op_array->opcodes
$1 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
$2 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
$3 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
$4 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
$5 = (opcode_handler_t) 0x8bf2f2 <ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER>
$6 = (opcode_handler_t) 0x8baffe <ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER>
$7 = (opcode_handler_t) 0x8baffe <ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER>
$8 = (opcode_handler_t) 0x87d09b <ZEND_RETURN_SPEC_CONST_HANDLER>
然后使用print_hash打印当前所有的变量
(gdb) print_hash executor_globals->symbol_table
0: _GET IS_ARRAY
1: _POST IS_ARRAY
2: _COOKIE IS_ARRAY
3: _FILES IS_ARRAY
4: argv IS_ARRAY
5: argc IS_LONG
6: _SERVER IS_ARRAY
7: aStr 15
8: aStrCopy 15
9: aStrCopy2 15
10: bStr 15
11: bStrRef 15
然后一条条的执行opcode,查看变量zval的变化.(最后我们通过分析函数确定这些变化)
(gdb) s
execute_ex (execute_data=0x7ffff6815030)
(gdb) p execute_data->opline->handler
$1 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 7
(gdb) n
360 }
(gdb) print_hash_on_index executor_globals->symbol_table 7
7: aStr
$2 = {value = {lval = 140737329057936, dval = 6.9533479374981184e-310,
counted = 0x7ffff6815090, str = 0x7ffff6815090, arr = 0x7ffff6815090,
obj = 0x7ffff6815090, res = 0x7ffff6815090, ref = 0x7ffff6815090,
ast = 0x7ffff6815090, zv = 0x7ffff6815090, ptr = 0x7ffff6815090,
ce = 0x7ffff6815090, func = 0x7ffff6815090, ww = {w1 = 4135669904,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
这里我们可以看出$aStr的类型还是IS_INDIRECT(15),与前面的test.php有所不同.
(gdb) p execute_data->opline->handler
$3 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 7
7: aStr
$5 = {value = {lval = 140737329057936, dval = 6.9533479374981184e-310,
counted = 0x7ffff6815090, str = 0x7ffff6815090, arr = 0x7ffff6815090,
obj = 0x7ffff6815090, res = 0x7ffff6815090, ref = 0x7ffff6815090,
ast = 0x7ffff6815090, zv = 0x7ffff6815090, ptr = 0x7ffff6815090,
ce = 0x7ffff6815090, func = 0x7ffff6815090, ww = {w1 = 4135669904,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 8
8: aStrCopy
$6 = {value = {lval = 140737329057952, dval = 6.9533479374989089e-310,
counted = 0x7ffff68150a0, str = 0x7ffff68150a0, arr = 0x7ffff68150a0,
obj = 0x7ffff68150a0, res = 0x7ffff68150a0, ref = 0x7ffff68150a0,
ast = 0x7ffff68150a0, zv = 0x7ffff68150a0, ptr = 0x7ffff68150a0,
ce = 0x7ffff68150a0, func = 0x7ffff68150a0, ww = {w1 = 4135669920,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行$aStrCopy = $aStr;后,两个变量的类型并没有发生变化.
(gdb) p execute_data->opline->handler
$7 = (opcode_handler_t) 0x8bf1f7 <ZEND_ASSIGN_SPEC_CV_CV_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 7
7: aStr
$8 = {value = {lval = 140737329057936, dval = 6.9533479374981184e-310,
counted = 0x7ffff6815090, str = 0x7ffff6815090, arr = 0x7ffff6815090,
obj = 0x7ffff6815090, res = 0x7ffff6815090, ref = 0x7ffff6815090,
ast = 0x7ffff6815090, zv = 0x7ffff6815090, ptr = 0x7ffff6815090,
ce = 0x7ffff6815090, func = 0x7ffff6815090, ww = {w1 = 4135669904,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
Cannot access memory at address 0x10
(gdb) print_hash_on_index executor_globals->symbol_table 8
8: aStrCopy
$9 = {value = {lval = 140737329057952, dval = 6.9533479374989089e-310,
counted = 0x7ffff68150a0, str = 0x7ffff68150a0, arr = 0x7ffff68150a0,
obj = 0x7ffff68150a0, res = 0x7ffff68150a0, ref = 0x7ffff68150a0,
ast = 0x7ffff68150a0, zv = 0x7ffff68150a0, ptr = 0x7ffff68150a0,
ce = 0x7ffff68150a0, func = 0x7ffff68150a0, ww = {w1 = 4135669920,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
Cannot access memory at address 0x10
(gdb) print_hash_on_index executor_globals->symbol_table 9
9: aStrCopy2
$10 = {value = {lval = 140737329057968, dval = 6.9533479374996994e-310,
counted = 0x7ffff68150b0, str = 0x7ffff68150b0, arr = 0x7ffff68150b0,
obj = 0x7ffff68150b0, res = 0x7ffff68150b0, ref = 0x7ffff68150b0,
ast = 0x7ffff68150b0, zv = 0x7ffff68150b0, ptr = 0x7ffff68150b0,
ce = 0x7ffff68150b0, func = 0x7ffff68150b0, ww = {w1 = 4135669936,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行$aStrCopy2 = $aStr;后3个便来类型也没有发生变化.
(gdb) p execute_data->opline->handler
$11 = (opcode_handler_t) 0x8b607a <ZEND_ASSIGN_SPEC_CV_CONST_HANDLER>
(gdb) n
(gdb) print_hash_on_index executor_globals->symbol_table 10
10: bStr
$15 = {value = {lval = 140737329057984, dval = 6.9533479375004899e-310,
counted = 0x7ffff68150c0, str = 0x7ffff68150c0, arr = 0x7ffff68150c0,
obj = 0x7ffff68150c0, res = 0x7ffff68150c0, ref = 0x7ffff68150c0,
ast = 0x7ffff68150c0, zv = 0x7ffff68150c0, ptr = 0x7ffff68150c0,
ce = 0x7ffff68150c0, func = 0x7ffff68150c0, ww = {w1 = 4135669952,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行$bStr = "bbb";后,前4个变量类型也没有发生变化.
(gdb) p execute_data->opline->handler
$16 = (opcode_handler_t) 0x8bf2f2 <ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 10
10: bStr
$17 = {value = {lval = 140737329057984, dval = 6.9533479375004899e-310,
counted = 0x7ffff68150c0, str = 0x7ffff68150c0, arr = 0x7ffff68150c0,
obj = 0x7ffff68150c0, res = 0x7ffff68150c0, ref = 0x7ffff68150c0,
ast = 0x7ffff68150c0, zv = 0x7ffff68150c0, ptr = 0x7ffff68150c0,
ce = 0x7ffff68150c0, func = 0x7ffff68150c0, ww = {w1 = 4135669952,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 11
11: bStrRef
$18 = {value = {lval = 140737329058000, dval = 6.9533479375012804e-310,
counted = 0x7ffff68150d0, str = 0x7ffff68150d0, arr = 0x7ffff68150d0,
obj = 0x7ffff68150d0, res = 0x7ffff68150d0, ref = 0x7ffff68150d0,
ast = 0x7ffff68150d0, zv = 0x7ffff68150d0, ptr = 0x7ffff68150d0,
ce = 0x7ffff68150d0, func = 0x7ffff68150d0, ww = {w1 = 4135669968,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行$bStrRef = & $bStr;后,类型也都没有发生变化.但是其他的值我们暂时没有比较,最后我们结合代码,来分析是那些字段发生了变化.
(gdb) p execute_data->opline->handler
$19 = (opcode_handler_t) 0x8baffe <ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 7
7: aStr
$20 = {value = {lval = 140737329057936, dval = 6.9533479374981184e-310,
counted = 0x7ffff6815090, str = 0x7ffff6815090, arr = 0x7ffff6815090,
obj = 0x7ffff6815090, res = 0x7ffff6815090, ref = 0x7ffff6815090,
ast = 0x7ffff6815090, zv = 0x7ffff6815090, ptr = 0x7ffff6815090,
ce = 0x7ffff6815090, func = 0x7ffff6815090, ww = {w1 = 4135669904,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash executor_globals->symbol_table
0: _GET IS_ARRAY
1: _POST IS_ARRAY
2: _COOKIE IS_ARRAY
3: _FILES IS_ARRAY
4: argv IS_ARRAY
5: argc IS_LONG
6: _SERVER IS_ARRAY
7: aStr 15
8: aStrCopy 15
9: aStrCopy2 15
10: bStr 15
11: bStrRef 15
执行完unset($aStr);我们仍然能够在符号表中找到它,说明unset并不是立即释放zval变量.
(gdb) p execute_data->opline->handler
$21 = (opcode_handler_t) 0x8baffe <ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDLER>
(gdb) print_hash_on_index executor_globals->symbol_table 10
10: bStr
$22 = {value = {lval = 140737329057984, dval = 6.9533479375004899e-310,
counted = 0x7ffff68150c0, str = 0x7ffff68150c0, arr = 0x7ffff68150c0,
obj = 0x7ffff68150c0, res = 0x7ffff68150c0, ref = 0x7ffff68150c0,
ast = 0x7ffff68150c0, zv = 0x7ffff68150c0, ptr = 0x7ffff68150c0,
ce = 0x7ffff68150c0, func = 0x7ffff68150c0, ww = {w1 = 4135669952,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 11
11: bStrRef
$23 = {value = {lval = 140737329058000, dval = 6.9533479375012804e-310,
counted = 0x7ffff68150d0, str = 0x7ffff68150d0, arr = 0x7ffff68150d0,
obj = 0x7ffff68150d0, res = 0x7ffff68150d0, ref = 0x7ffff68150d0,
ast = 0x7ffff68150d0, zv = 0x7ffff68150d0, ptr = 0x7ffff68150d0,
ce = 0x7ffff68150d0, func = 0x7ffff68150d0, ww = {w1 = 4135669968,
w2 = 32767}}, u1 = {v = {type = 15 '\017', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 15}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行完unset($bStr);,$bStr也没有发生什么明显变化.
(gdb) p execute_data->opline->handler
$24 = (opcode_handler_t) 0x87d09b <ZEND_RETURN_SPEC_CONST_HANDLER>
(gdb) n
353 if (EXPECTED(ret > 0)) {
(gdb) print_hash executor_globals->symbol_table
0: _GET IS_ARRAY
1: _POST IS_ARRAY
2: _COOKIE IS_ARRAY
3: _FILES IS_ARRAY
4: argv IS_ARRAY
5: argc IS_LONG
6: _SERVER IS_ARRAY
7: aStr IS_UNDEF
8: aStrCopy IS_STRING aaa
9: aStrCopy2 IS_STRING aaa
10: bStr 10
11: bStrRef IS_UNDEF
(gdb) print_zval_note
$30 = "IS_RESOURCE 9 IS_REFERENCE 10 IS_CONSTANT 11 IS_CONSTANT_AST 12 _IS_BOOL 13 IS_CALLABLE 14 IS_INDIRECT 15 IS_PTR 17"
(gdb) print_hash_on_index executor_globals->symbol_table 7
7: aStr
$25 = {value = {lval = 140737329057936, dval = 6.9533479374981184e-310,
counted = 0x7ffff6815090, str = 0x7ffff6815090, arr = 0x7ffff6815090,
obj = 0x7ffff6815090, res = 0x7ffff6815090, ref = 0x7ffff6815090,
ast = 0x7ffff6815090, zv = 0x7ffff6815090, ptr = 0x7ffff6815090,
ce = 0x7ffff6815090, func = 0x7ffff6815090, ww = {w1 = 4135669904,
w2 = 32767}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 0}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 8
8: aStrCopy
$26 = {value = {lval = 140737328986688, dval = 6.9533479339779995e-310,
counted = 0x7ffff6803a40, str = 0x7ffff6803a40, arr = 0x7ffff6803a40,
obj = 0x7ffff6803a40, res = 0x7ffff6803a40, ref = 0x7ffff6803a40,
ast = 0x7ffff6803a40, zv = 0x7ffff6803a40, ptr = 0x7ffff6803a40,
ce = 0x7ffff6803a40, func = 0x7ffff6803a40, ww = {w1 = 4135598656,
w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 9
9: aStrCopy2
$27 = {value = {lval = 140737328986688, dval = 6.9533479339779995e-310,
counted = 0x7ffff6803a40, str = 0x7ffff6803a40, arr = 0x7ffff6803a40,
obj = 0x7ffff6803a40, res = 0x7ffff6803a40, ref = 0x7ffff6803a40,
ast = 0x7ffff6803a40, zv = 0x7ffff6803a40, ptr = 0x7ffff6803a40,
ce = 0x7ffff6803a40, func = 0x7ffff6803a40, ww = {w1 = 4135598656,
w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 10
10: bStr
$28 = {value = {lval = 140737329455272, dval = 6.9533479571291251e-310,
counted = 0x7ffff68760a8, str = 0x7ffff68760a8, arr = 0x7ffff68760a8,
obj = 0x7ffff68760a8, res = 0x7ffff68760a8, ref = 0x7ffff68760a8,
ast = 0x7ffff68760a8, zv = 0x7ffff68760a8, ptr = 0x7ffff68760a8,
ce = 0x7ffff68760a8, func = 0x7ffff68760a8, ww = {w1 = 4136067240,
w2 = 32767}}, u1 = {v = {type = 10 '\n', type_flags = 4 '\004',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 1034}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
(gdb) print_hash_on_index executor_globals->symbol_table 11
11: bStrRef
$29 = {value = {lval = 140737329058000, dval = 6.9533479375012804e-310,
counted = 0x7ffff68150d0, str = 0x7ffff68150d0, arr = 0x7ffff68150d0,
obj = 0x7ffff68150d0, res = 0x7ffff68150d0, ref = 0x7ffff68150d0,
ast = 0x7ffff68150d0, zv = 0x7ffff68150d0, ptr = 0x7ffff68150d0,
ce = 0x7ffff68150d0, func = 0x7ffff68150d0, ww = {w1 = 4135669968,
w2 = 32767}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000',
const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 0}, u2 = {
var_flags = 4294967295, next = 4294967295, cache_slot = 4294967295,
lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295,
fe_iter_idx = 4294967295}}
执行完退出流程,发现zval的值发生了相应的变化.
赋值函数的分析(能力有限,暂时分析到这里)