赞
踩
原文来自微信公众号:编程语言Lab- 基于语义的编译器测试
搜索关注编程语言Lab 公众号:HW-PLLab获取编程语言更多技术内容
如果读者想了解更多有关 类型系统相关的技术内容,欢迎加入 编程语言社区 SIG-程序分析。
加入方式:文末有小助手微信,添加并备注加入 SIG-程序分析。
SIG-程序分析技术沙龙回顾|Semantic-based Compiler Testinghttps://www.bilibili.com/video/BV1zY411b7uG?
今天的题目是编译器的测试,我先解释一下为什么这个题目本身有意义。
第一点,这个问题很重要。之前的讲座讲到了很多程序上面的漏洞,但这些只是单个程序的问题。如果编译器本身是有漏洞的话,那么理论上所有的程序都有可能有漏洞。因为所有的程序最终是在编译器上运行,如果编译器本身运行会出错,那么你没法保证这个程序的正确性。也就是说,所有程序正确性的分析是先基于这个编译器本身的正确性来做的。
第二点,现在很多新的语言层出不穷,比如智慧合约、Rust、Kotlin 等等。而且就算是传统的程序,比如像 C 或者 C++,一般认为 C 和 C++ 这种程序用了这么多年,几百万的程序员都写了各种各样的 C 程序,那么 GCC 或者 LLVM 这些编译器本身应该是比较正确了。但实际并不是,这几年大家发现这些编译器里面还是有存在很多漏洞。像刚才说的,这些漏洞理论上有可能会造成很大的麻烦。
最后一点,Tony Hoare,一个很知名的计算机科学家(大家学习过的 Quick Sort 就是他发明的),在 2003 年的时候给大家提了一系列的 Grand Challenges。他当时认为计算机科学,主要是指软件工程,形式化方法这些,缺少一些大的目标。就是说我们发了很多文章做了很多工具,但确实解决了很多很大的问题吗?好像并没有我们想象的那样。他就提了几个特别远大的目标,意思是说你们做计算机科学的最好像做物理的一样,如果有一些远大的目标,那么我们就可以保证这个领域不断的向前迈进。他其中提出的一个目标,就是我们要验证我们的编译器。如果我们连自己写的编译器,这是作为我们所有程序的基础,都做不到可以验证它的正确性,那么你要说我们可以验证别的大的系统的正确性,这个事情他觉得有可能需要打一个问号。
总的来说,我想说的是编译器的正确性这个问题本身很有意义。同时,为了解决这个问题,我们发展了一系列方法。这些方法本身也可能对解决别的问题有帮助。
现在我们就来定义这个问题:一个编译器,你可以想象它就是一个函数,这个函数的输入就是你的源代码。比如说它可以是一个 Java 程序,或者你对智慧合约有些了解的话,它就可以是一个 Solidity 的程序。它的输出就是对应的机器码,对于 Java 来说是 JVM 上的 bytecode,对于 Solidity 来说,就是 EVM 上的 opcode。
那么我们的问题就是,给定任意一个程序,比如说 Java 程序,我们要保证原程序和通过编译器编译出来的程序,他们俩的行为是一致的。这个就是我们想要解决的问题。这个问题本身很难,因为给定一个语言以后,比如说 Java 或者 Solidity,你能写的程序有无穷多个,而我们要求任意一个程序进来,我们都能保证最后编译出来的程序是对的。
具体到怎么解决这个问题,我们需要解决三个比较小的问题。
我要怎么定义原程序的语义是什么?程序员用某一种语言来写某一个程序的时候,肯定是想达到某种目标的,肯定是基于对这个程序语言的某种理解来写的这个程序。这个理论上你可以理解为这就是我的这个程序想要的语义。
同时编译出来的那个程序,比如说 Java 的 bytecode、EVM 的 opcode,它本身也是有语义的,它的语义实际就代表了机器去执行这个东西,它应该执行出来什么效果。问题是我们要怎么定义这些编译完了的程序的语义?
如果我可以很好地定义原程序的语义和编译完的程序的语义。接下来问题就是我们要怎么证明给定任何一个程序,原程序的语义和对应的编译完的程序的语义是一样的?这里就要定义一个等价关系。这个等价关系要怎么定义,也是一个很大的问题。这个问题有很长很长的历史,很多人提出过很多不同的等价关系。
因为编译器是个很重要的东西。这么多年大家还是花了不少的精力想解决这个问题的。特别是 Tony Hoare 2003 年提出这个 Grand Challenge 以后的几年里,还是有不少的科学家做了很多尝试的。当然最后的效果不是特别好,过了几年以后大家慢慢就转战别的问题了。
# 方法
编译器的正确性的研究可以粗略的分成两类,形式化的证明,和测试。
## 形式化的方法
其中一个比较乌托邦的做法是,用一个很形式化的方法来很严谨的解决这个问题。比如说用形式化的方法来定义一个形式化的程序的语义,同时用一个同样的形式化的方法来定义对应的机器语言的语义,然后用一个理论证明的方法来证明这两个东西永远是一致的。当然这种做法代价很大。因为你需要:
第一步,把语言的语义完整地定义出来;
第二步,把机器语言的语义完整地定义出来;
第三步,通过人工的方法去证明对于任何程序,这俩都是等价的。
比较成功的一个例子是 CompCert verified compiler,它可以定义一个小小的 C 的子集叫 Clight。然后你给我任意一个 Clight 里面写的程序,或者说你只用某一部分 C 里面的语言特性的话,同时如果对应的出来的机器码是运行在 PowerPC assembly code 上面的,那么我可以保证这个结果是对的。
这已经是做的最好的一个结果了。比较苛刻一点地说,这个东西实际上很难有用。因为就算你可以做到这种程度,但程序语言是一个不断发展的东西。比如说 Java,这几年它发展很快,各种新的版本层出不穷。如果你通过这种方法来证明 JVM 的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。