PHP内核对符号的处理

PHP内核在编译PHP脚本的过程中,会把符号的名字转化为符号对应的数据存储空间的地址(注意,不是符号的地址,而是符号对应的数据存储空间的地址)。

我们知道,opline的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef union _znode_op {
uint32_t constant;
uint32_t var;
uint32_t num;
} znode_op;

struct _zend_op {
const void *handler;
znode_op op1;
znode_op op2;
znode_op result;
uint32_t extended_value;
uint32_t lineno;
zend_uchar opcode;
zend_uchar op1_type;
zend_uchar op2_type;
zend_uchar result_type;
};

znode_op这个结构,它是一个uint32_t类型的数字,可以用来存放和操作数地址有关的东西。这也就意味着,编译完PHP脚本之后,可以丢弃这些符号的名字,都转换成地址即可。

而名字到地址的转换,核心函数是lookup_cv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int lookup_cv(zend_string *name) /* {{{ */{
zend_op_array *op_array = CG(active_op_array);
int i = 0;
zend_ulong hash_value = zend_string_hash_val(name);

while (i < op_array->last_var) {
if (ZSTR_H(op_array->vars[i]) == hash_value
&& zend_string_equals(op_array->vars[i], name)) {
return EX_NUM_TO_VAR(i);
}
i++;
}
i = op_array->last_var;
op_array->last_var++;
if (op_array->last_var > CG(context).vars_size) {
CG(context).vars_size += 16; /* FIXME */
op_array->vars = erealloc(op_array->vars, CG(context).vars_size * sizeof(zend_string*));
}

op_array->vars[i] = zend_string_copy(name);
return EX_NUM_TO_VAR(i);
}

这段代码,就是用来确定一个个CV变量在栈中的存储地址。也就意味着,栈的大小,在编译期间就确定好了。