function gen_vm($def, $skel) { global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames, $vm_op_flags; // Load definition file $in = @file($def); if (!$in) { die("ERROR: Can not open definition file '{$def}'\n"); } // We need absolute path to definition file to use it in #line directives $definition_file = realpath($def); // Load skeleton file $skl = @file($skel); if (!$skl) { die("ERROR: Can not open skeleton file '{$skel}'\n"); } // We need absolute path to skeleton file to use it in #line directives $skeleton_file = realpath($skel); // Parse definition file into tree $lineno = 0; $handler = null; $helper = null; $max_opcode_len = 0; $max_opcode = 0; $export = array(); foreach ($in as $line) { ++$lineno; if (strpos($line, "ZEND_VM_HANDLER(") === 0) { // Parsing opcode handler's definition if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n"); } $code = (int) $m[1]; $op = $m[2]; $len = strlen($op); $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2); $flags = $flags1 | $flags2 << 8; if (isset($m[6])) { $flags |= parse_ext_spec($def, $lineno, $m[6]); } if ($len > $max_opcode_len) { $max_opcode_len = $len; } if ($code > $max_opcode) { $max_opcode = $code; } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n"); } if (isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n"); } $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags); $opnames[$op] = $code; $handler = $code; $helper = null; $list[$lineno] = array("handler" => $handler); } else { if (strpos($line, "ZEND_VM_HELPER(") === 0) { // Parsing helper's definition if (preg_match("/^ZEND_VM_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n"); } $helper = $m[1]; $op1 = parse_operand_spec($def, $lineno, $m[2], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[3], $flags2); if (isset($helpers[$helper])) { die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n"); } $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => null, "code" => ""); $handler = null; $list[$lineno] = array("helper" => $helper); } else { if (strpos($line, "ZEND_VM_HELPER_EX(") === 0) { // Parsing helper with parameter definition if (preg_match("/^ZEND_VM_HELPER_EX\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*(.*)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n"); } $helper = $m[1]; $op1 = parse_operand_spec($def, $lineno, $m[2], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[3], $flags2); $param = $m[4]; if (isset($helpers[$helper])) { die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n"); } // Store parameter $params[$param] = 1; $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => ""); $handler = null; $list[$lineno] = array("helper" => $helper); } else { if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n"); } if (!isset($opnames[$m[2]])) { die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n"); } $export[] = array("handler", $m[1], $m[2]); } else { if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n"); } if (!isset($helpers[$m[2]])) { die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n"); } $export[] = array("helper", $m[1], $m[2]); } else { if (strpos($line, "ZEND_VM_DEFINE_OP(") === 0) { if (preg_match("/^ZEND_VM_DEFINE_OP\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*\\);/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_DEFINE_OP definition.\n"); } $code = (int) $m[1]; $op = $m[2]; $len = strlen($op); if ($len > $max_opcode_len) { $max_opcode_len = $len; } if ($code > $max_opcode) { $max_opcode = $code; } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n"); } if (isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n"); } $opcodes[$code] = array("op" => $op, "code" => ""); $opnames[$op] = $code; } else { if ($handler !== null) { // Add line of code to current opcode handler $opcodes[$handler]["code"] .= $line; } else { if ($helper !== null) { // Add line of code to current helper $helpers[$helper]["code"] .= $line; } } } } } } } } } ksort($opcodes); // Search for opcode handlers those are used by other opcode handlers foreach ($opcodes as $dsc) { if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) { $op = $m[1]; if (!isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n"); } $code = $opnames[$op]; $opcodes[$code]['use'] = 1; } } // Generate opcode #defines (zend_vm_opcodes.h) $code_len = strlen((string) $max_opcode); $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n"); // Insert header out($f, $GLOBALS['header_text']); fputs($f, "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n"); fputs($f, "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n"); fputs($f, "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n"); fputs($f, "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n"); fputs($f, "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n"); fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n"); fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n"); fputs($f, "\n"); foreach ($vm_op_flags as $name => $val) { fprintf($f, "#define %-24s 0x%08x\n", $name, $val); } fputs($f, "\n"); fputs($f, "BEGIN_EXTERN_C()\n\n"); fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n"); fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);\n\n"); fputs($f, "END_EXTERN_C()\n\n"); foreach ($opcodes as $code => $dsc) { $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT); $op = str_pad($dsc["op"], $max_opcode_len); fputs($f, "#define {$op} {$code}\n"); } $code = str_pad((string) $max_opcode, $code_len, " ", STR_PAD_LEFT); $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len); fputs($f, "\n#define {$op} {$code}\n"); fputs($f, "\n#endif\n"); fclose($f); echo "zend_vm_opcodes.h generated successfully.\n"; // zend_vm_opcodes.c $f = fopen(__DIR__ . "/zend_vm_opcodes.c", "w+") or die("ERROR: Cannot create zend_vm_opcodes.c\n"); // Insert header out($f, $GLOBALS['header_text']); fputs($f, "#include <stdio.h>\n"); fputs($f, "#include <zend.h>\n\n"); fputs($f, "static const char *zend_vm_opcodes_names[" . ($max_opcode + 1) . "] = {\n"); for ($i = 0; $i <= $max_opcode; $i++) { fputs($f, "\t" . (isset($opcodes[$i]["op"]) ? '"' . $opcodes[$i]["op"] . '"' : "NULL") . ",\n"); } fputs($f, "};\n\n"); fputs($f, "static uint32_t zend_vm_opcodes_flags[" . ($max_opcode + 1) . "] = {\n"); for ($i = 0; $i <= $max_opcode; $i++) { fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0); } fputs($f, "};\n\n"); fputs($f, "ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {\n"); fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n"); fputs($f, "}\n"); fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {\n"); fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n"); fputs($f, "}\n"); fclose($f); echo "zend_vm_opcodes.c generated successfully.\n"; // Generate zend_vm_execute.h $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n"); $executor_file = realpath(__DIR__ . "/zend_vm_execute.h"); // Insert header out($f, $GLOBALS['header_text']); out($f, "#ifdef ZEND_WIN32\n"); // Suppress free_op1 warnings on Windows out($f, "# pragma warning(once : 4101)\n"); if (ZEND_VM_SPEC) { // Suppress (<non-zero constant> || <expression>) warnings on windows out($f, "# pragma warning(once : 6235)\n"); // Suppress (<zero> && <expression>) warnings on windows out($f, "# pragma warning(once : 6237)\n"); // Suppress (<non-zero constant> && <expression>) warnings on windows out($f, "# pragma warning(once : 6239)\n"); // Suppress (<expression> && <non-zero constant>) warnings on windows out($f, "# pragma warning(once : 6240)\n"); // Suppress (<non-zero constant> || <non-zero constant>) warnings on windows out($f, "# pragma warning(once : 6285)\n"); // Suppress (<non-zero constant> || <expression>) warnings on windows out($f, "# pragma warning(once : 6286)\n"); // Suppress constant with constant comparison warnings on windows out($f, "# pragma warning(once : 6326)\n"); } out($f, "#endif\n"); // Support for ZEND_USER_OPCODE out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n"); for ($i = 0; $i < 255; ++$i) { out($f, "\t(user_opcode_handler_t)NULL,\n"); } out($f, "\t(user_opcode_handler_t)NULL\n};\n\n"); out($f, "static zend_uchar zend_user_opcodes[256] = {"); for ($i = 0; $i < 255; ++$i) { if ($i % 16 == 1) { out($f, "\n\t"); } out($f, "{$i},"); } out($f, "255\n};\n\n"); // Generate specialized executor gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers"); // Generate zend_vm_get_opcode_handler() function out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n"); out($f, "{\n"); if (!ZEND_VM_SPEC) { out($f, "\treturn zend_opcode_handlers[opcode];\n"); } else { out($f, "\t\tstatic const int zend_vm_decode[] = {\n"); out($f, "\t\t\t_UNUSED_CODE, /* 0 */\n"); out($f, "\t\t\t_CONST_CODE, /* 1 = IS_CONST */\n"); out($f, "\t\t\t_TMP_CODE, /* 2 = IS_TMP_VAR */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 3 */\n"); out($f, "\t\t\t_VAR_CODE, /* 4 = IS_VAR */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 5 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 6 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 7 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 8 = IS_UNUSED */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 9 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 10 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 11 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 12 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 13 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 14 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 15 */\n"); out($f, "\t\t\t_CV_CODE /* 16 = IS_CV */\n"); out($f, "\t\t};\n"); out($f, "\t\treturn zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];\n"); } out($f, "}\n\n"); // Generate zend_vm_get_opcode_handler() function out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n"); out($f, "{\n"); out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n"); out($f, "}\n\n"); // Generate zend_vm_call_opcode_handler() function if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); out($f, "\tint ret;\n"); out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n"); out($f, "\tconst zend_op *orig_opline = opline;\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n"); out($f, "\tzend_execute_data *orig_execute_data = execute_data;\n"); out($f, "\texecute_data = ex;\n"); out($f, "#else\n"); out($f, "\tzend_execute_data *execute_data = ex;\n"); out($f, "#endif\n"); out($f, "\n"); out($f, "\tLOAD_OPLINE();\n"); out($f, "#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n"); out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f, "\tif (EXPECTED(opline)) {\n"); out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n"); out($f, "\t\tSAVE_OPLINE();\n"); out($f, "\t} else {\n"); out($f, "\t\tret = -1;\n"); out($f, "\t}\n"); out($f, "#else\n"); out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f, "\tSAVE_OPLINE();\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n"); out($f, "\texecute_data = orig_execute_data;\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n"); out($f, "\topline = orig_opline;\n"); out($f, "#endif\n"); out($f, "\treturn ret;\n"); out($f, "}\n\n"); } else { out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); out($f, "\tzend_error_noreturn(E_CORE_ERROR, \"zend_vm_call_opcode_handler() is not supported\");\n"); out($f, "\treturn 0;\n"); out($f, "}\n\n"); } // Export handlers and helpers if (count($export) > 0 && ZEND_VM_KIND != ZEND_VM_KIND_CALL) { out($f, "#undef OPLINE\n"); out($f, "#undef DCL_OPLINE\n"); out($f, "#undef USE_OPLINE\n"); out($f, "#undef LOAD_OPLINE\n"); out($f, "#undef LOAD_NEXT_OPLINE\n"); out($f, "#undef SAVE_OPLINE\n"); out($f, "#define OPLINE EX(opline)\n"); out($f, "#define DCL_OPLINE\n"); out($f, "#define USE_OPLINE const zend_op *opline = EX(opline);\n"); out($f, "#define LOAD_OPLINE()\n"); out($f, "#define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n"); out($f, "#define SAVE_OPLINE()\n"); out($f, "#undef HANDLE_EXCEPTION\n"); out($f, "#undef HANDLE_EXCEPTION_LEAVE\n"); out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n"); out($f, "#undef ZEND_VM_CONTINUE\n"); out($f, "#undef ZEND_VM_RETURN\n"); out($f, "#undef ZEND_VM_ENTER\n"); out($f, "#undef ZEND_VM_LEAVE\n"); out($f, "#undef ZEND_VM_DISPATCH\n"); out($f, "#define ZEND_VM_CONTINUE() return 0\n"); out($f, "#define ZEND_VM_RETURN() return -1\n"); out($f, "#define ZEND_VM_ENTER() return 1\n"); out($f, "#define ZEND_VM_LEAVE() return 2\n"); out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n"); out($f, "\n"); } foreach ($export as $dsk) { list($kind, $func, $name) = $dsk; out($f, "ZEND_API int {$func}("); if ($kind == "handler") { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); $code = $opcodes[$opnames[$name]]['code']; } else { $h = $helpers[$name]; if ($h['param'] == null) { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); } else { out($f, $h['param'] . " ZEND_OPCODE_HANDLER_ARGS_DC)\n"); } $code = $h['code']; } $done = 0; if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { if ($kind == "handler") { $op = $opcodes[$opnames[$name]]; if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } else { if ($helpers[$name]["param"] == null) { $h = $helpers[$name]; if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } } } if (!$done) { gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name); } } fclose($f); echo "zend_vm_execute.h generated successfully.\n"; }
function gen_vm($def, $skel) { global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames; // Load definition file $in = @file($def); if (!$in) { die("ERROR: Can not open definition file '{$def}'\n"); } // We need absolute path to definition file to use it in #line directives $definition_file = realpath($def); // Load skeleton file $skl = @file($skel); if (!$skl) { die("ERROR: Can not open skeleton file '{$skel}'\n"); } // We need absolute path to skeleton file to use it in #line directives $skeleton_file = realpath($skel); // Parse definition file into tree $lineno = 0; $handler = null; $helper = null; $max_opcode_len = 0; $max_opcode = 0; $export = array(); foreach ($in as $line) { ++$lineno; if (strpos($line, "ZEND_VM_HANDLER(") === 0) { // Parsing opcode handler's definition if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n"); } $code = (int) $m[1]; $op = $m[2]; $len = strlen($op); $op1 = array_flip(explode("|", $m[3])); $op2 = array_flip(explode("|", $m[4])); if ($len > $max_opcode_len) { $max_opcode_len = $len; } if ($code > $max_opcode) { $max_opcode = $code; } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n"); } if (isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n"); } $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => ""); $opnames[$op] = $code; $handler = $code; $helper = null; $list[$lineno] = array("handler" => $handler); } else { if (strpos($line, "ZEND_VM_HELPER(") === 0) { // Parsing helper's definition if (preg_match("/^ZEND_VM_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n"); } $helper = $m[1]; $op1 = array_flip(explode("|", $m[2])); $op2 = array_flip(explode("|", $m[3])); if (isset($helpers[$helper])) { die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n"); } $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => null, "code" => ""); $handler = null; $list[$lineno] = array("helper" => $helper); } else { if (strpos($line, "ZEND_VM_HELPER_EX(") === 0) { // Parsing helper with parameter definition if (preg_match("/^ZEND_VM_HELPER_EX\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*([A-Z|]+)\\s*,\\s*(.*)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n"); } $helper = $m[1]; $op1 = array_flip(explode("|", $m[2])); $op2 = array_flip(explode("|", $m[3])); $param = $m[4]; if (isset($helpers[$helper])) { die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n"); } // Store parameter $params[$param] = 1; $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => ""); $handler = null; $list[$lineno] = array("helper" => $helper); } else { if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n"); } if (!isset($opnames[$m[2]])) { die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n"); } $export[] = array("handler", $m[1], $m[2]); } else { if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n"); } if (!isset($helpers[$m[2]])) { die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n"); } $export[] = array("helper", $m[1], $m[2]); } else { if ($handler !== null) { // Add line of code to current opcode handler $opcodes[$handler]["code"] .= $line; } else { if ($helper !== null) { // Add line of code to current helper $helpers[$helper]["code"] .= $line; } } } } } } } } ksort($opcodes); // Search for opcode handlers those are used by other opcode handlers foreach ($opcodes as $dsc) { if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) { $op = $m[1]; if (!isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n"); } $code = $opnames[$op]; $opcodes[$code]['use'] = 1; } } // Generate opcode #defines (zend_vm_opcodes.h) $code_len = strlen((string) $max_opcode); $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n"); // Insert header out($f, $GLOBALS['header_text']); foreach ($opcodes as $code => $dsc) { $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT); $op = str_pad($dsc["op"], $max_opcode_len); fputs($f, "#define {$op} {$code}\n"); } fclose($f); echo "zend_vm_opcodes.h generated successfully.\n"; // Generate zend_vm_execute.h $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n"); $executor_file = realpath(__DIR__ . "/zend_vm_execute.h"); // Insert header out($f, $GLOBALS['header_text']); // Suppress free_op1 warnings on Windows out($f, "#ifdef ZEND_WIN32\n# pragma warning(once : 4101)\n#endif\n"); // Support for ZEND_USER_OPCODE out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n"); for ($i = 0; $i < 255; ++$i) { out($f, "\t(user_opcode_handler_t)NULL,\n"); } out($f, "\t(user_opcode_handler_t)NULL\n};\n\n"); out($f, "static zend_uchar zend_user_opcodes[256] = {"); for ($i = 0; $i < 255; ++$i) { if ($i % 16 == 1) { out($f, "\n\t"); } out($f, "{$i},"); } out($f, "255\n};\n\n"); // Generate specialized executor gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers", 0); // Generate un-specialized executor if (ZEND_VM_OLD_EXECUTOR) { out($f, "\n/* Old executor */\n\n"); out($f, "#undef ZEND_VM_CONTINUE\n\n"); out($f, "#undef ZEND_VM_RETURN\n\n"); out($f, "#undef ZEND_VM_ENTER\n\n"); out($f, "#undef ZEND_VM_LEAVE\n\n"); out($f, "#undef ZEND_VM_DISPATCH\n\n"); out($f, "#undef ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL\n\n"); gen_executor($f, $skl, 0, ZEND_VM_KIND_CALL, "old_execute", "zend_vm_use_old_executor", 1); } // Generate zend_vm_get_opcode_handler() function out($f, "static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)\n"); out($f, "{\n"); if (!ZEND_VM_SPEC) { out($f, "\treturn zend_opcode_handlers[opcode];\n"); } else { if (ZEND_VM_OLD_EXECUTOR) { out($f, "\tif (zend_vm_old_executor) {\n"); out($f, "\t\treturn zend_opcode_handlers[opcode];\n"); out($f, "\t} else {\n"); } out($f, "\t\tstatic const int zend_vm_decode[] = {\n"); out($f, "\t\t\t_UNUSED_CODE, /* 0 */\n"); out($f, "\t\t\t_CONST_CODE, /* 1 = IS_CONST */\n"); out($f, "\t\t\t_TMP_CODE, /* 2 = IS_TMP_VAR */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 3 */\n"); out($f, "\t\t\t_VAR_CODE, /* 4 = IS_VAR */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 5 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 6 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 7 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 8 = IS_UNUSED */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 9 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 10 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 11 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 12 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 13 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 14 */\n"); out($f, "\t\t\t_UNUSED_CODE, /* 15 */\n"); out($f, "\t\t\t_CV_CODE /* 16 = IS_CV */\n"); out($f, "\t\t};\n"); out($f, "\t\treturn zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];\n"); if (ZEND_VM_OLD_EXECUTOR) { out($f, "\t}\n"); } } out($f, "}\n\n"); // Generate zend_vm_get_opcode_handler() function out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n"); out($f, "{\n"); out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n"); out($f, "}\n\n"); // Export handlers and helpers if (count($export) > 0 && !ZEND_VM_OLD_EXECUTOR && ZEND_VM_KIND != ZEND_VM_KIND_CALL) { out($f, "#undef OPLINE\n"); out($f, "#undef DCL_OPLINE\n"); out($f, "#undef USE_OPLINE\n"); out($f, "#undef LOAD_OPLINE\n"); out($f, "#undef SAVE_OPLINE\n"); out($f, "#define OPLINE EX(opline)\n"); out($f, "#define DCL_OPLINE\n"); out($f, "#define USE_OPLINE zend_op *opline = EX(opline);\n"); out($f, "#define LOAD_OPLINE()\n"); out($f, "#define SAVE_OPLINE()\n"); out($f, "#undef CHECK_EXCEPTION\n"); out($f, "#undef HANDLE_EXCEPTION\n"); out($f, "#undef HANDLE_EXCEPTION_LEAVE\n"); out($f, "#define CHECK_EXCEPTION() LOAD_OPLINE()\n"); out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n"); out($f, "#undef ZEND_VM_CONTINUE\n"); out($f, "#undef ZEND_VM_RETURN\n"); out($f, "#undef ZEND_VM_ENTER\n"); out($f, "#undef ZEND_VM_LEAVE\n"); out($f, "#undef ZEND_VM_DISPATCH\n"); out($f, "#undef ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL\n\n"); out($f, "#define ZEND_VM_CONTINUE() return 0\n"); out($f, "#define ZEND_VM_RETURN() return 1\n"); out($f, "#define ZEND_VM_ENTER() return 2\n"); out($f, "#define ZEND_VM_LEAVE() return 3\n"); out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n"); out($f, "#define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL execute_data TSRMLS_CC\n\n"); } foreach ($export as $dsk) { list($kind, $func, $name) = $dsk; out($f, "ZEND_API int {$func}("); if ($kind == "handler") { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); $code = $opcodes[$opnames[$name]]['code']; } else { $h = $helpers[$name]; if ($h['param'] == null) { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); } else { out($f, $h['param'] . ", ZEND_OPCODE_HANDLER_ARGS)\n"); } $code = $h['code']; } $done = 0; if (ZEND_VM_OLD_EXECUTOR) { if ($kind == "handler") { out($f, "{\n\treturn " . $name . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } else { if ($helpers[$name]["param"] == null) { out($f, "{\n\treturn " . $name . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } } else { if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { if ($kind == "handler") { $op = $opcodes[$opnames[$name]]; if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } else { if ($helpers[$name]["param"] == null) { $h = $helpers[$name]; if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } } } } if (!$done) { gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name); } } fclose($f); echo "zend_vm_execute.h generated successfully.\n"; }
function gen_vm($def, $skel) { global $definition_file, $skeleton_file, $executor_file, $op_types, $list, $opcodes, $helpers, $params, $opnames, $vm_op_flags, $used_extra_spec; // Load definition file $in = @file($def); if (!$in) { die("ERROR: Can not open definition file '{$def}'\n"); } // We need absolute path to definition file to use it in #line directives $definition_file = realpath($def); // Load skeleton file $skl = @file($skel); if (!$skl) { die("ERROR: Can not open skeleton file '{$skel}'\n"); } // We need absolute path to skeleton file to use it in #line directives $skeleton_file = realpath($skel); // Parse definition file into tree $lineno = 0; $handler = null; $helper = null; $max_opcode_len = 0; $max_opcode = 0; $extra_num = 256; $export = array(); foreach ($in as $line) { ++$lineno; if (strpos($line, "ZEND_VM_HANDLER(") === 0) { // Parsing opcode handler's definition if (preg_match("/^ZEND_VM_HANDLER\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?(,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HANDLER definition.\n"); } $code = (int) $m[1]; $op = $m[2]; $len = strlen($op); $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2); $flags = $flags1 | $flags2 << 8; if (!empty($m[6])) { $flags |= parse_ext_spec($def, $lineno, $m[6]); } if ($len > $max_opcode_len) { $max_opcode_len = $len; } if ($code > $max_opcode) { $max_opcode = $code; } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n"); } if (isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n"); } $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags); if (isset($m[8])) { $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[8]); if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) { $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"]; } if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) { $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"]; } } $opnames[$op] = $code; $handler = $code; $helper = null; $list[$lineno] = array("handler" => $handler); } else { if (strpos($line, "ZEND_VM_TYPE_SPEC_HANDLER(") === 0) { // Parsing opcode handler's definition if (preg_match("/^ZEND_VM_TYPE_SPEC_HANDLER\\(\\s*([A-Z_]+)\\s*,\\s*([^,]+),\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(,\\s*([A-Z_|]+)\\s*)?(,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_TYPE_HANDLER_HANDLER definition.\n"); } $orig_op = $m[1]; if (!isset($opnames[$orig_op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$orig_op}' is not defined.\n"); } $orig_code = $opnames[$orig_op]; $condition = $m[2]; $code = $extra_num++; $op = $m[3]; $op1 = parse_operand_spec($def, $lineno, $m[4], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[5], $flags2); $flags = $flags1 | $flags2 << 8; if (!empty($m[7])) { $flags |= parse_ext_spec($def, $lineno, $m[7]); } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$code}' is already defined.\n"); } $opcodes[$orig_code]['type_spec'][$code] = $condition; $used_extra_spec["TYPE"] = 1; $opcodes[$code] = array("op" => $op, "op1" => $op1, "op2" => $op2, "code" => "", "flags" => $flags); if (isset($m[9])) { $opcodes[$code]["spec"] = parse_spec_rules($def, $lineno, $m[9]); if (isset($opcodes[$code]["spec"]["NO_CONST_CONST"])) { $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_NO_CONST_CONST"]; } if (isset($opcodes[$code]["spec"]["COMMUTATIVE"])) { $opcodes[$code]["flags"] |= $vm_op_flags["ZEND_VM_COMMUTATIVE"]; } } $opnames[$op] = $code; $handler = $code; $helper = null; $list[$lineno] = array("handler" => $handler); } else { if (strpos($line, "ZEND_VM_HELPER(") === 0 || strpos($line, "ZEND_VM_INLINE_HELPER(") === 0) { // Parsing helper's definition if (preg_match("/^ZEND_VM(_INLINE)?_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_|]+)\\s*,\\s*([A-Z_|]+)\\s*(?:,\\s*SPEC\\(([A-Z_|=,]+)\\)\\s*)?(?:,\\s*([^)]*)\\s*)?\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_HELPER definition.\n"); } $inline = !empty($m[1]); $helper = $m[2]; $op1 = parse_operand_spec($def, $lineno, $m[3], $flags1); $op2 = parse_operand_spec($def, $lineno, $m[4], $flags2); $param = isset($m[6]) ? $m[6] : null; if (isset($helpers[$helper])) { die("ERROR ({$def}:{$lineno}): Helper with name '{$helper}' is already defined.\n"); } // Store parameters foreach (explode(",", $param) as $p) { $p = trim($p); if ($p !== "") { $params[$p] = 1; } } $helpers[$helper] = array("op1" => $op1, "op2" => $op2, "param" => $param, "code" => "", "inline" => $inline); if (!empty($m[5])) { $helpers[$helper]["spec"] = parse_spec_rules($def, $lineno, $m[5]); } $handler = null; $list[$lineno] = array("helper" => $helper); } else { if (strpos($line, "ZEND_VM_EXPORT_HANDLER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HANDLER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HANDLER definition.\n"); } if (!isset($opnames[$m[2]])) { die("ERROR ({$def}:{$lineno}): opcode '{$m[2]}' is not defined.\n"); } $export[] = array("handler", $m[1], $m[2]); } else { if (strpos($line, "ZEND_VM_EXPORT_HELPER(") === 0) { if (preg_match("/^ZEND_VM_EXPORT_HELPER\\(\\s*([A-Za-z_]+)\\s*,\\s*([A-Za-z_]+)\\s*\\)/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_EXPORT_HELPER definition.\n"); } if (!isset($helpers[$m[2]])) { die("ERROR ({$def}:{$lineno}): helper '{$m[2]}' is not defined.\n"); } $export[] = array("helper", $m[1], $m[2]); } else { if (strpos($line, "ZEND_VM_DEFINE_OP(") === 0) { if (preg_match("/^ZEND_VM_DEFINE_OP\\(\\s*([0-9]+)\\s*,\\s*([A-Z_]+)\\s*\\);/", $line, $m) == 0) { die("ERROR ({$def}:{$lineno}): Invalid ZEND_VM_DEFINE_OP definition.\n"); } $code = (int) $m[1]; $op = $m[2]; $len = strlen($op); if ($len > $max_opcode_len) { $max_opcode_len = $len; } if ($code > $max_opcode) { $max_opcode = $code; } if (isset($opcodes[$code])) { die("ERROR ({$def}:{$lineno}): Opcode with code '{$code}' is already defined.\n"); } if (isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is already defined.\n"); } $opcodes[$code] = array("op" => $op, "code" => ""); $opnames[$op] = $code; } else { if ($handler !== null) { // Add line of code to current opcode handler $opcodes[$handler]["code"] .= $line; } else { if ($helper !== null) { // Add line of code to current helper $helpers[$helper]["code"] .= $line; } } } } } } } } } ksort($opcodes); // Search for opcode handlers those are used by other opcode handlers foreach ($opcodes as $dsc) { if (preg_match("/ZEND_VM_DISPATCH_TO_HANDLER\\(\\s*([A-Z_]*)\\s*\\)/m", $dsc["code"], $m)) { $op = $m[1]; if (!isset($opnames[$op])) { die("ERROR ({$def}:{$lineno}): Opcode with name '{$op}' is not defined.\n"); } $code = $opnames[$op]; $opcodes[$code]['use'] = 1; } } // Generate opcode #defines (zend_vm_opcodes.h) $code_len = strlen((string) $max_opcode); $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n"); // Insert header out($f, HEADER_TEXT); fputs($f, "#ifndef ZEND_VM_OPCODES_H\n#define ZEND_VM_OPCODES_H\n\n"); fputs($f, "#define ZEND_VM_SPEC\t\t" . ZEND_VM_SPEC . "\n"); fputs($f, "#define ZEND_VM_LINES\t\t" . ZEND_VM_LINES . "\n"); fputs($f, "#define ZEND_VM_KIND_CALL\t" . ZEND_VM_KIND_CALL . "\n"); fputs($f, "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n"); fputs($f, "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n"); fputs($f, "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n"); fputs($f, "\n"); foreach ($vm_op_flags as $name => $val) { fprintf($f, "#define %-24s 0x%08x\n", $name, $val); } fputs($f, "#define ZEND_VM_OP1_FLAGS(flags) (flags & 0xff)\n"); fputs($f, "#define ZEND_VM_OP2_FLAGS(flags) ((flags >> 8) & 0xff)\n"); fputs($f, "\n"); fputs($f, "BEGIN_EXTERN_C()\n\n"); fputs($f, "ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);\n"); fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode);\n\n"); fputs($f, "END_EXTERN_C()\n\n"); foreach ($opcodes as $code => $dsc) { $code = str_pad((string) $code, $code_len, " ", STR_PAD_LEFT); $op = str_pad($dsc["op"], $max_opcode_len); if ($code <= $max_opcode) { fputs($f, "#define {$op} {$code}\n"); } } $code = str_pad((string) $max_opcode, $code_len, " ", STR_PAD_LEFT); $op = str_pad("ZEND_VM_LAST_OPCODE", $max_opcode_len); fputs($f, "\n#define {$op} {$code}\n"); fputs($f, "\n#endif\n"); fclose($f); echo "zend_vm_opcodes.h generated successfully.\n"; // zend_vm_opcodes.c $f = fopen(__DIR__ . "/zend_vm_opcodes.c", "w+") or die("ERROR: Cannot create zend_vm_opcodes.c\n"); // Insert header out($f, HEADER_TEXT); fputs($f, "#include <stdio.h>\n"); fputs($f, "#include <zend.h>\n\n"); fputs($f, "static const char *zend_vm_opcodes_names[" . ($max_opcode + 1) . "] = {\n"); for ($i = 0; $i <= $max_opcode; $i++) { fputs($f, "\t" . (isset($opcodes[$i]["op"]) ? '"' . $opcodes[$i]["op"] . '"' : "NULL") . ",\n"); } fputs($f, "};\n\n"); fputs($f, "static uint32_t zend_vm_opcodes_flags[" . ($max_opcode + 1) . "] = {\n"); for ($i = 0; $i <= $max_opcode; $i++) { fprintf($f, "\t0x%08x,\n", isset($opcodes[$i]["flags"]) ? $opcodes[$i]["flags"] : 0); } fputs($f, "};\n\n"); fputs($f, "ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {\n"); fputs($f, "\treturn zend_vm_opcodes_names[opcode];\n"); fputs($f, "}\n"); fputs($f, "ZEND_API uint32_t zend_get_opcode_flags(zend_uchar opcode) {\n"); fputs($f, "\treturn zend_vm_opcodes_flags[opcode];\n"); fputs($f, "}\n"); fclose($f); echo "zend_vm_opcodes.c generated successfully.\n"; // Generate zend_vm_execute.h $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n"); $executor_file = realpath(__DIR__ . "/zend_vm_execute.h"); // Insert header out($f, HEADER_TEXT); out($f, "#ifdef ZEND_WIN32\n"); // Suppress free_op1 warnings on Windows out($f, "# pragma warning(disable : 4101)\n"); if (ZEND_VM_SPEC) { // Suppress (<non-zero constant> || <expression>) warnings on windows out($f, "# pragma warning(once : 6235)\n"); // Suppress (<zero> && <expression>) warnings on windows out($f, "# pragma warning(once : 6237)\n"); // Suppress (<non-zero constant> && <expression>) warnings on windows out($f, "# pragma warning(once : 6239)\n"); // Suppress (<expression> && <non-zero constant>) warnings on windows out($f, "# pragma warning(once : 6240)\n"); // Suppress (<non-zero constant> || <non-zero constant>) warnings on windows out($f, "# pragma warning(once : 6285)\n"); // Suppress (<non-zero constant> || <expression>) warnings on windows out($f, "# pragma warning(once : 6286)\n"); // Suppress constant with constant comparison warnings on windows out($f, "# pragma warning(once : 6326)\n"); } out($f, "#endif\n"); // Support for ZEND_USER_OPCODE out($f, "static user_opcode_handler_t zend_user_opcode_handlers[256] = {\n"); for ($i = 0; $i < 255; ++$i) { out($f, "\t(user_opcode_handler_t)NULL,\n"); } out($f, "\t(user_opcode_handler_t)NULL\n};\n\n"); out($f, "static zend_uchar zend_user_opcodes[256] = {"); for ($i = 0; $i < 255; ++$i) { if ($i % 16 == 1) { out($f, "\n\t"); } out($f, "{$i},"); } out($f, "255\n};\n\n"); // Generate specialized executor gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_init_opcodes_handlers"); // Generate zend_vm_get_opcode_handler() function out($f, "static const void *zend_vm_get_opcode_handler_ex(uint32_t spec, const zend_op* op)\n"); out($f, "{\n"); if (!ZEND_VM_SPEC) { out($f, "\treturn zend_opcode_handlers[spec];\n"); } else { out($f, "\tstatic const int zend_vm_decode[] = {\n"); out($f, "\t\t_UNUSED_CODE, /* 0 */\n"); out($f, "\t\t_CONST_CODE, /* 1 = IS_CONST */\n"); out($f, "\t\t_TMP_CODE, /* 2 = IS_TMP_VAR */\n"); out($f, "\t\t_UNUSED_CODE, /* 3 */\n"); out($f, "\t\t_VAR_CODE, /* 4 = IS_VAR */\n"); out($f, "\t\t_UNUSED_CODE, /* 5 */\n"); out($f, "\t\t_UNUSED_CODE, /* 6 */\n"); out($f, "\t\t_UNUSED_CODE, /* 7 */\n"); out($f, "\t\t_UNUSED_CODE, /* 8 = IS_UNUSED */\n"); out($f, "\t\t_UNUSED_CODE, /* 9 */\n"); out($f, "\t\t_UNUSED_CODE, /* 10 */\n"); out($f, "\t\t_UNUSED_CODE, /* 11 */\n"); out($f, "\t\t_UNUSED_CODE, /* 12 */\n"); out($f, "\t\t_UNUSED_CODE, /* 13 */\n"); out($f, "\t\t_UNUSED_CODE, /* 14 */\n"); out($f, "\t\t_UNUSED_CODE, /* 15 */\n"); out($f, "\t\t_CV_CODE /* 16 = IS_CV */\n"); out($f, "\t};\n"); out($f, "\tuint32_t offset = 0;\n"); out($f, "\tif (spec & SPEC_RULE_OP1) offset = offset * 5 + zend_vm_decode[op->op1_type];\n"); out($f, "\tif (spec & SPEC_RULE_OP2) offset = offset * 5 + zend_vm_decode[op->op2_type];\n"); if (isset($used_extra_spec["OP_DATA"])) { out($f, "\tif (spec & SPEC_RULE_OP_DATA) offset = offset * 5 + zend_vm_decode[(op + 1)->op1_type];\n"); } if (isset($used_extra_spec["RETVAL"])) { out($f, "\tif (spec & SPEC_RULE_RETVAL) offset = offset * 2 + (op->result_type != IS_UNUSED);\n"); } if (isset($used_extra_spec["QUICK_ARG"])) { out($f, "\tif (spec & SPEC_RULE_QUICK_ARG) offset = offset * 2 + (op->op2.num < MAX_ARG_FLAG_NUM);\n"); } if (isset($used_extra_spec["SMART_BRANCH"])) { out($f, "\tif (spec & SPEC_RULE_SMART_BRANCH) {\n"); out($f, "\t\toffset = offset * 3;\n"); out($f, "\t\tif ((op+1)->opcode == ZEND_JMPZ) {\n"); out($f, "\t\t\toffset += 1;\n"); out($f, "\t\t} else if ((op+1)->opcode == ZEND_JMPNZ) {\n"); out($f, "\t\t\toffset += 2;\n"); out($f, "\t\t}\n"); out($f, "\t}\n"); } if (isset($used_extra_spec["DIM_OBJ"])) { out($f, "\tif (spec & SPEC_RULE_DIM_OBJ) {\n"); out($f, "\t\toffset = offset * 3;\n"); out($f, "\t\tif (op->extended_value == ZEND_ASSIGN_DIM) {\n"); out($f, "\t\t\toffset += 1;\n"); out($f, "\t\t} else if (op->extended_value == ZEND_ASSIGN_OBJ) {\n"); out($f, "\t\t\toffset += 2;\n"); out($f, "\t\t}\n"); out($f, "\t}\n"); } out($f, "\treturn zend_opcode_handlers[(spec & SPEC_START_MASK) + offset];\n"); } out($f, "}\n\n"); out($f, "static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op)\n"); out($f, "{\n"); if (!ZEND_VM_SPEC) { out($f, "\treturn zend_vm_get_opcode_handler_ex(opcode, op);\n"); } else { out($f, "\treturn zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op);\n"); } out($f, "}\n\n"); // Generate zend_vm_get_opcode_handler() function out($f, "ZEND_API void zend_vm_set_opcode_handler(zend_op* op)\n"); out($f, "{\n"); out($f, "\top->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);\n"); out($f, "}\n\n"); // Generate zend_vm_set_opcode_handler_ex() function out($f, "ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint32_t op2_info, uint32_t res_info)\n"); out($f, "{\n"); out($f, "\tzend_uchar opcode = zend_user_opcodes[op->opcode];\n"); if (!ZEND_VM_SPEC) { out($f, "\top->handler = zend_vm_get_opcode_handler_ex(opcode, op);\n"); } else { out($f, "\tuint32_t spec = zend_spec_handlers[opcode];\n"); if (isset($used_extra_spec["TYPE"])) { out($f, "\tswitch (opcode) {\n"); foreach ($opcodes as $code => $dsc) { if (isset($dsc['type_spec'])) { $orig_op = $dsc['op']; out($f, "\t\tcase {$orig_op}:\n"); $first = true; foreach ($dsc['type_spec'] as $code => $condition) { if ($first) { out($f, "\t\t\tif ({$condition}) {\n"); $first = false; } else { out($f, "\t\t\t} else if ({$condition}) {\n"); } $spec_dsc = $opcodes[$code]; if (isset($spec_dsc["spec"]["NO_CONST_CONST"])) { out($f, "\t\t\t\tif (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {\n"); out($f, "\t\t\t\t\tbreak;\n"); out($f, "\t\t\t\t}\n"); } out($f, "\t\t\t\tspec = {$spec_dsc['spec_code']};\n"); if (isset($spec_dsc["spec"]["COMMUTATIVE"])) { out($f, "\t\t\t\tif (op->op1_type > op->op2_type) {\n"); out($f, "\t\t\t\t\tzend_swap_operands(op);\n"); out($f, "\t\t\t\t}\n"); } } if (!$first) { out($f, "\t\t\t}\n"); } out($f, "\t\t\tbreak;\n"); } } out($f, "\t\tdefault:\n"); out($f, "\t\t\tbreak;\n"); out($f, "\t}\n"); } out($f, "\top->handler = zend_vm_get_opcode_handler_ex(spec, op);\n"); } out($f, "}\n\n"); // Generate zend_vm_call_opcode_handler() function if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); out($f, "\tint ret;\n"); out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n"); out($f, "\tconst zend_op *orig_opline = opline;\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n"); out($f, "\tzend_execute_data *orig_execute_data = execute_data;\n"); out($f, "\texecute_data = ex;\n"); out($f, "#else\n"); out($f, "\tzend_execute_data *execute_data = ex;\n"); out($f, "#endif\n"); out($f, "\n"); out($f, "\tLOAD_OPLINE();\n"); out($f, "#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n"); out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f, "\tif (EXPECTED(opline)) {\n"); out($f, "\t\tret = execute_data != ex ? (int)(execute_data->prev_execute_data != ex) + 1 : 0;\n"); out($f, "\t\tSAVE_OPLINE();\n"); out($f, "\t} else {\n"); out($f, "\t\tret = -1;\n"); out($f, "\t}\n"); out($f, "#else\n"); out($f, "\tret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f, "\tSAVE_OPLINE();\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_FP_GLOBAL_REG\n"); out($f, "\texecute_data = orig_execute_data;\n"); out($f, "#endif\n"); out($f, "#ifdef ZEND_VM_IP_GLOBAL_REG\n"); out($f, "\topline = orig_opline;\n"); out($f, "#endif\n"); out($f, "\treturn ret;\n"); out($f, "}\n\n"); } else { out($f, "ZEND_API int zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); out($f, "\tzend_error_noreturn(E_CORE_ERROR, \"zend_vm_call_opcode_handler() is not supported\");\n"); out($f, "\treturn 0;\n"); out($f, "}\n\n"); } // Export handlers and helpers if (count($export) > 0 && ZEND_VM_KIND != ZEND_VM_KIND_CALL) { out($f, "#undef OPLINE\n"); out($f, "#undef DCL_OPLINE\n"); out($f, "#undef USE_OPLINE\n"); out($f, "#undef LOAD_OPLINE\n"); out($f, "#undef LOAD_NEXT_OPLINE\n"); out($f, "#undef SAVE_OPLINE\n"); out($f, "#define OPLINE EX(opline)\n"); out($f, "#define DCL_OPLINE\n"); out($f, "#define USE_OPLINE const zend_op *opline = EX(opline);\n"); out($f, "#define LOAD_OPLINE()\n"); out($f, "#define LOAD_NEXT_OPLINE() ZEND_VM_INC_OPCODE()\n"); out($f, "#define SAVE_OPLINE()\n"); out($f, "#undef HANDLE_EXCEPTION\n"); out($f, "#undef HANDLE_EXCEPTION_LEAVE\n"); out($f, "#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f, "#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n"); out($f, "#undef ZEND_VM_CONTINUE\n"); out($f, "#undef ZEND_VM_RETURN\n"); out($f, "#undef ZEND_VM_ENTER\n"); out($f, "#undef ZEND_VM_LEAVE\n"); out($f, "#undef ZEND_VM_DISPATCH\n"); out($f, "#define ZEND_VM_CONTINUE() return 0\n"); out($f, "#define ZEND_VM_RETURN() return -1\n"); out($f, "#define ZEND_VM_ENTER() return 1\n"); out($f, "#define ZEND_VM_LEAVE() return 2\n"); out($f, "#define ZEND_VM_INTERRUPT() return zend_interrupt_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); out($f, "#define ZEND_VM_DISPATCH(opcode, opline) return zend_vm_get_opcode_handler(opcode, opline)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n\n"); out($f, "\n"); } foreach ($export as $dsk) { list($kind, $func, $name) = $dsk; out($f, "ZEND_API int {$func}("); if ($kind == "handler") { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); $code = $opcodes[$opnames[$name]]['code']; } else { $h = $helpers[$name]; if ($h['param'] == null) { out($f, "ZEND_OPCODE_HANDLER_ARGS)\n"); } else { out($f, $h['param'] . " ZEND_OPCODE_HANDLER_ARGS_DC)\n"); } $code = $h['code']; } $done = 0; if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { if ($kind == "handler") { $op = $opcodes[$opnames[$name]]; if (isset($op['op1']["ANY"]) && isset($op['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } else { if ($helpers[$name]["param"] == null) { $h = $helpers[$name]; if (isset($h['op1']["ANY"]) && isset($h['op2']["ANY"])) { out($f, "{\n\treturn " . $name . (ZEND_VM_SPEC ? "_SPEC" : "") . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n}\n\n"); $done = 1; } } } } if (!$done) { gen_code($f, 0, ZEND_VM_KIND_CALL, 1, $code, 'ANY', 'ANY', $name); } } fclose($f); echo "zend_vm_execute.h generated successfully.\n"; }