Python跨版本字节码解释器
项目描述
x-python
这是一个用Python编写的CPython字节码解释器。
您可以使用它来
了解CPython内部工作原理,因为它模拟了这些
尝试额外的操作码,或改变运行时环境的方法
作为沙箱环境尝试执行片段
有一个Python程序可以运行多个版本的Python字节码。
在动态模糊测试或协同执行中用于分析
能够从Python 3.10或Python 2.7解释器(假设没有新运行时的高级异步功能)运行到2.4的简单Python字节码,我认为这很酷。
此外,我还发现调试器中的沙箱环境很有趣。(注意:目前环境并没有很好地沙箱化,但我正在努力实现这一点,这有点具有挑战性。)
由于在调试器内部有一个单独的执行和跟踪栈,您可以在调试会话中间尝试一些事情,而不会影响真实执行。另一方面,如果一系列执行结果成功,则可能在某些情况下将其(复制)返回到CPython的执行栈。
我已经将 trepan3k 集成到这个解释器中,因此您有一个类似于pdb/gdb的调试器,并且可以单步执行字节码指令。
为了实验更快地支持跟踪回调,例如调试器中使用的回调,增加了一条指令以支持快速断点和在特定指令上设置断点,该指令不是在行边界上。我相信这可以也应该被移植回CPython,并且会带来好处。(Python 3.8支持保存额外的帧信息,原始操作码就存储在这里。只需要< cite>BRKPT cite>操作码)
尽管这还遥不可及,但假设你想添加一个竞态检测器?在这里用Python原型化它可能更容易。(这假设解释器很好地支持线程,我怀疑它不支持)
上述内容暗示了另一个未探索的途径,即混合解释和直接CPython执行。事实上,现在确实存在这样的错误,但它们将被转化为特性。有些函数或类你可能不想在慢速解释器下运行,而有些你确实想运行。
示例
想知道当你编写一些简单代码时运行了哪些指令?试试这个
$ xpython -vc "x, y = 2, 3; x **= y" INFO:xpython.vm:L. 1 @ 0: LOAD_CONST (2, 3) INFO:xpython.vm: @ 2: UNPACK_SEQUENCE 2 INFO:xpython.vm: @ 4: STORE_NAME (2) x INFO:xpython.vm: @ 6: STORE_NAME (3) y INFO:xpython.vm:L. 1 @ 8: LOAD_NAME x INFO:xpython.vm: @ 10: LOAD_NAME y INFO:xpython.vm: @ 12: INPLACE_POWER (2, 3) INFO:xpython.vm: @ 14: STORE_NAME (8) x INFO:xpython.vm: @ 16: LOAD_CONST None INFO:xpython.vm: @ 18: RETURN_VALUE (None)
选项-c与Python的标志(作为字符串传递的程序)相同,而-v也是Python标志的类似物。在这里,它显示了运行的字节码指令。
注意,上述动态跟踪中的反汇编比Python的dis模块中的静态反汇编器显示的要多一些。特别是,STORE_NAME指令显示了存储的值,例如指令偏移量4处的“2”,存储在名称x中。同样,INPLACE_POWER显示了操作数2和3,这是如何得出下一个指令的操作数,即STORE_NAME的值8。
还想了解更多,比如执行堆栈和块堆栈?添加另一个v
$ xpython -vvc "x, y = 2, 3; x **= y" DEBUG:xpython.vm:make_frame: code=<code object <module> at 0x7f8018507db0, file "<string x, y = 2, 3; x **= y>", line 1>, callargs={}, f_globals=(<class 'dict'>, 140188140947488), f_locals=(<class 'NoneType'>, 93856967704000) DEBUG:xpython.vm:<Frame at 0x7f80184c1e50: '<string x, y = 2, 3; x **= y>':1 @-1> DEBUG:xpython.vm: frame.stack: [] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm:L. 1 @ 0: LOAD_CONST (2, 3) <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [(2, 3)] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 2: UNPACK_SEQUENCE 2 <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [3, 2] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 4: STORE_NAME (2) x <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [3] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 6: STORE_NAME (3) y <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm:L. 1 @ 8: LOAD_NAME x <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [2] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 10: LOAD_NAME y <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [2, 3] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 12: INPLACE_POWER (2, 3) <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [8] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 14: STORE_NAME (8) x <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 16: LOAD_CONST None <module> in <string x, y = 2, 3; x **= y>:1 DEBUG:xpython.vm: frame.stack: [None] DEBUG:xpython.vm: blocks : [] INFO:xpython.vm: @ 18: RETURN_VALUE (None) <module> in <string x, y = 2, 3; x **= y>:1
想在终端中看到这种颜色化?使用这个trepan-xpy -x:
假设你有Python 2.4字节码(或其他字节码),但你在运行Python 3.7?
$ xpython -v test/examples/assign-2.4.pyc INFO:xpython.vm:L. 1 @ 0: LOAD_CONST (2, 3) INFO:xpython.vm: @ 3: UNPACK_SEQUENCE 2 INFO:xpython.vm: @ 6: STORE_NAME (2) x INFO:xpython.vm: @ 9: STORE_NAME (3) y INFO:xpython.vm:L. 2 @ 12: LOAD_NAME x INFO:xpython.vm: @ 15: LOAD_NAME y INFO:xpython.vm: @ 18: INPLACE_POWER (2, 3) INFO:xpython.vm: @ 19: STORE_NAME (8) x INFO:xpython.vm: @ 22: LOAD_CONST None INFO:xpython.vm: @ 25: RETURN_VALUE (None)
这里没有太大的变化,只是从3.6之后指令是2字节而不是1-或3字节指令。
上述示例显示了直线代码,所以你看到了所有指令。但不要将此与来自
$ xpython -vc "x = 6 if __name__ != '__main__' else 10" INFO:xpython.vm:L. 1 @ 0: LOAD_NAME __name__ INFO:xpython.vm: @ 2: LOAD_CONST __main__ INFO:xpython.vm: @ 4: COMPARE_OP ('__main__', '__main__') != INFO:xpython.vm: @ 6: POP_JUMP_IF_FALSE 12 ^^ Note jump below INFO:xpython.vm: @ 12: LOAD_CONST 10 INFO:xpython.vm: @ 14: STORE_NAME (10) x INFO:xpython.vm: @ 16: LOAD_CONST None INFO:xpython.vm: @ 18: RETURN_VALUE (None)
想了解更多状态和控制?请参阅trepan-xpy。
状态
目前支持Python版本3.10到3.2以及2.7到2.4的字节码。Python的最新版本并没有实现所有操作码。这只是我的许多兴趣之一,因此支持可能很糟糕。我使用资金来帮助我指导我在修复问题上的注意力,这些问题在这个项目中非常多。
Byterun,这是基于这个的,非常棒。但它以微妙的方式作弊。
想用CPython编写一个非常小的解释器吗?
# get code somehow exec(code)
这种作弊方式有点低俗,但这种作弊方式在 Byterun 中以更微妙的方式进行。例如,上面提到的例子依赖于内置函数 exec 来完成所有工作,而 Byterun 则依赖于各种类似类型的内置函数来支持指令码的解释。实际上,如果你正在 解释 的代码是上述代码,那么 Byterun 将使用其内置函数在 exec 函数调用内部运行代码,因此代码内部的所有字节码都不会被用于解释。
此外,像 exec 这样的内置函数以及其他内置模块会影响解释器命名空间。因此,这两个命名空间就会混合在一起。
其中一个例子是关于 import 的。参见 https://github.com/nedbat/byterun/issues/26。但还有其他情况。虽然我们还没有解决第26号问题中提到的 import 问题,但我们已经解决了类似的问题。
一些内置函数和 inpsect 模块需要内置类型,如 cell、traceback 或 frame 对象,并且它们不能使用相应的解释器类。在 Byterun 中,这是这样一个例子:类 __init__ 函数不会被追踪,因为依赖于内置函数 __build_class__。而 __build_class__ 需要一个本地函数,而不是一个可由解释器追踪的函数。参见 https://github.com/nedbat/byterun/pull/20。
此外,Byterun 在接受字节码指令码时比较宽松,这可能对于某些Python版本无效,但对于另一个版本可能是有效的。我想这是可以的,因为你不期望无效的指令码出现在有效的字节码中。然而,当提取过程不准确时,可能会意外或错误地出现通过某种提取过程获得的代码。
相比之下,x-python 对接受的指令码更为严格。
Byterun 需要进行彻底的改造,以便能够扩展到支持更多 Python 版本的字节码,并能够在不同的 Python 版本之间运行字节码。具体来说,如果你期望运行的不是解释器正在运行的字节码,或者在新“wordcode”字节码上运行“字节”取向的字节码,或者相反,那么你就不能依赖 Python 的 dis 模块。
相比之下,在 x-python 中,正在解释的版本和正在运行的 Python 版本之间存在明确的区别。对指令码有更严格的控制,并且为每个 Python 版本保留指令码的实现。因此,当有无效内容时,我们会提前警告。你可以使用 Python 3.10(在很大程度上)运行回 Python 2.4 的字节码,这很令人惊讶,因为 3.10 的本地字节码每条指令是2个字节,而 2.4 的每条指令是1个或3个字节。
“在很大程度上”是指,正如上面提到的,解释器始终使用 Python 内置和库,而且大部分没有太大变化。通常,由于许多底层内置函数是相同的,解释器可以使用解释器内部。例如,内置函数如 range() 就是这种方式支持的。
因此,从比 Python 解释器使用的 Python 版本更新的版本解释字节码也是可能的。尽管 Python 2.7 不支持关键字参数或格式字符串,但它仍然可以解释使用这些构造创建的字节码。
这是可能的,因为这些特定功能更多的是语法糖,而不是运行时的扩展。例如,格式字符串基本上映射到使用 2.7 上可用的 format() 函数。
但像异步 I/O 和并发原语这样的新功能在旧版本中并不存在。因此,需要模拟这些功能,如果有兴趣或支持,这也是可能的。
您可以运行许多Python用于自身测试的测试,我也这么做!而且其中大多数都有效。目前,这个程序在Python 3.4或更早版本上运行最佳,那时的Python生活要简单得多。它在Python自己的测试套件中运行超过300个测试而没有任何问题。对于Python 3.6,这个数字下降到大约237;Python 3.9的情况更糟。
历史
这是Byterun的一个分支,而Byterun是一个纯Python实现的Python字节码执行虚拟机。Ned Batchelder开始这个项目(基于Paul Swartz的工作),以便更好地理解字节码,从而修复coverage.py中的分支覆盖率错误。
项目详情
下载文件
下载适合您平台文件的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。
源分布
构建分布
x-python-1.5.1.tar.gz的哈希
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a662895cc46be1928c3862718c34b7e2f8141150d588b0185aaae8c75dbcaff2 |
|
MD5 | ba748ff23aa857bd0333ad943e8e03ca |
|
BLAKE2b-256 | 95d349da66dc361dd8e2a80a4904acc5644748a90f45a82a7ae08cb2dc9f1b75 |