PHP 操作码与实际执行的二进制代码有何关系?
Posted
技术标签:
【中文标题】PHP 操作码与实际执行的二进制代码有何关系?【英文标题】:how does PHP opcode relate to the actually executed binary code? 【发布时间】:2012-01-10 13:51:02 【问题描述】:test.php 为纯文本:
<?php
$x = "a";
echo $x;
test.php 作为操作码:
debian:~ php -d vld.active=1 -d vld.execute=0 -f test.php
Finding entry points
Branch analysis from position: 0
Return found
filename: /root/test.php
function name: (null)
number of ops: 5
compiled vars: !0 = $x
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > EXT_STMT
1 ASSIGN !0, 'a'
3 2 EXT_STMT
3 ECHO !0
4 4 > RETURN 1
branch: # 0; line: 2- 4; sop: 0; eop: 4
path #1: 0,
test.php 作为二进制表示:
debian:~ php -d apc.stat=0 -r "
require '/root/test.php';
echo PHP_EOL;
echo chunk_split(bin2hex(
apc_bin_dump(array('/root/test.php'))
),64);
"
(跳过 test.php 的回显输出)
b110000001000000325dedaa64d801bca2f73027abf0d5ab67f3023901000000
2c0000000a000000871000000300000000000000000000004c0000005b000000
8a0200008a020000650000002f726f6f742f746573742e7068700002070f9c00
00000000000000000000000000000000000000000000000000000000000100fa
000000fe00000005000000050000007c02000001000000100000000100000000
00000000000000ffffffff0000000000000000000000000000000000000000ff
ffffffeb00000000000000000000000000000000000000ffffffff0000000000
00000001000000000000002f726f6f742f746573742e7068700001000000204a
3308080000000000000000000000000000000000000008000000000000000000
0000000000000000000008000000000000000000000000000000000000000000
00000200000065000000204a3308040000000000000001000000000000000000
00001000000000000000100000000100000006000000010000007a0200000100
00000100000006000000000000000200000026000000204a3308080000000000
0000000000000000000000000000080000000000000000000000000000000000
0000080000000000000000000000000000000000000000000000030000006500
0000900f34080800000000000000000000000000000000000000100000000000
0000100000000100000006000000080000000000000000000000000000000000
0000000000000300000028000000204a33080800000000000000000000000000
00000000000001000000010000002c70d7b6010000000100d7b6080000000000
000000000000000000000000000000000000040000003e000000610088020000
01000000bd795900780000000000000000000000000000000000000000000000
[ ... a lot of lines just containing 0s ... ]
0000000000000038000000c30000007f0000007a010000830000007c0200008f
0000003c000000400000004400000008
现在我想了解更多关于操作码如何转换为二进制表示的信息。
经过编辑和澄清的问题:
操作码如何翻译成二进制版本? 你能看到 'a' 到 !0 的分配吗? ECHO 语句是否在某处以及它的输出?
我在二进制版本中发现很少有模式可以提示操作码的逐行表示。
(“2f726f6f742f746573742e706870”是“/root/test.php”的十六进制表示)
编辑:
当行长设置为 4 字节并在不同程序之间进行比较时,十六进制表示揭示了模式。
...
00000002 // 2 seems to be something like the "line number"
00000065 // seems to increase by 1 for every subsequent statement.
00000040 //
06330808 // seems to mark the START of a statement
00000000
00000000
00000000
00000000
00000001 //
00000012 // In a program with three echo statements,
03000007 // this block was present three times. With mild
00000001 // changes that seem to represent the spot where
00000006 // the output-string is located.
00000008 //
00000000
00000000
00000000
00000000
00000000
00000002 // 2 seems to be something like the "line number"
00000028 //
00000020 //
4a330808 // seems to mark the END of a statement
00000000
00000000
00000000
00000000
00000008 // repeating between (echo-)statements
00000000
00000000
00000000
00000000
00000008 // repeating between (echo-)statements
...
但我对虚拟机如何在这种级别上工作的知识太薄弱,无法真正正确地分析并将其链接到 C 代码。
编辑:
Does PHP have a virtual machine like Java?
Is the Zend engine embeddable outside of PHP?
【问题讨论】:
【参考方案1】:好问题...
更新:操作码由 PHP 虚拟机(Zend 引擎)直接执行。看起来好像它们是由 ./Zend/zend_vm_execute.h 中定义的不同处理函数执行的
有关如何执行 Zend 操作码的更多信息,请参阅 the architecture of the Zend Engine。
这些资源可能会有所帮助:
http://php.net/manual/en/internals2.opcodes.list.php
http://www.php.net/manual/en/internals2.opcodes.ops.php
另外,我将查看 PECL VLD Source 以获取更多线索...
http://pecl.php.net/package/vld
http://derickrethans.nl/projects.html#vld
此外,编写 VLD Pecl 扩展的作者可能会有所帮助: Derick Rethans、Andrei Zmievski 或 Marcus Börger
他们的电子邮件地址位于扩展源中 srm_oparray.c 的顶部。
更新:发现更多线索
在 PHP 5.3.8 中,我发现了操作码执行位置的三个线索:
./Zend/zend_execute.c:1270
ZEND_API void execute_internal
./Zend/zend.c:1214:ZEND_API int zend_execute_scripts(int type TSRMLS_DC, zval **retval, int file_count, ...)
./Zend/zend.c:1236: zend_execute(EG(active_op_array) TSRMLS_CC);
./Zend/zend_vm_gen.php
我找不到 zend_execute() 的定义,但我猜它可能是用 ./zend_vm_gen.php 生成的
我想我找到了……
./Zend/zend_vm_execute.h:42
ZEND_API void execute(zend_op_array *op_array TSRMLS_DC)
我可能是错的,但看起来所有的操作码处理程序也是在 ./Zend/zend_vm_execute.h 中定义的。
请参阅 ./Zend/zend_vm_execute.h:2413 以获取“整数加法”操作码的示例。
【讨论】:
已经检查过那些资源;因为我在上面使用 VLD。它们似乎只是涵盖了从 PHP 代码到操作码的过渡。 更新...添加了电子邮件地址建议。 这些文件看起来很有希望... ./Zend/zend_vm_opcodes.h ./Zend/zend_vm_def.h 更新:在PHP源码中发现了更多线索。 更新:找到 Zend VM 操作码处理函数的定义。【参考方案2】:apc_bin_dump() 返回内存中缓存条目的原始表示。
它返回apc_bd_t struct的内容。
这个结构是一个 apc_bd_entry_t 数组,带有一些用于错误检测的校验和。
apc_bd_entry_t 包含一个apc_cache_entry_value_t。
您可以查看apc_bin_dump 和apc_bin_load 内部函数以了解转储和加载是如何进行的。
【讨论】:
以上是关于PHP 操作码与实际执行的二进制代码有何关系?的主要内容,如果未能解决你的问题,请参考以下文章
2018-04-26 《鸟哥的Linux私房菜 基础学习篇(第四版)》 第21章 软件安装_原始码与Tarball 笔记