“你如何知道你使用的编译器生成的机器代码完全符合C语言代码的功能,并且编译器是完全确定性的?”
对于这个问题,我完全没有回答,因为我一直认为编译器是理所当然的。它接受代码并输出机器代码。我怎么能测试编译器是否实际上添加了我没有要求的功能?或者更危险的是,以与我预期不同的方式实现代码?
我知道这可能并不是每个人都面临的问题,而且答案可能只是...“你没有选择,必须处理它”。然而,在嵌入式环境中工作时,你会完全信任你的编译器。我如何向自己和QA证明我在这样做时是正确的?
您可以在任何层面上应用该论点:您信任第三方库吗? 您信任操作系统吗? 您信任处理器吗?
一个很好的例子,说明这可能是一个有效的关注点,就是肯·汤普森(Ken Thompson)如何在原始的“登录”程序中放置了后门 ... 并修改了C编译器,以便即使重新编译登录程序仍然会得到后门。有关更多详细信息,请参见此帖子。
类似的问题已经针对加密算法提出--我们怎么知道DES中没有NSA用于窥探的后门?
最终,您必须决定是否信任构建基础设施足够,以不必担心它,否则您必须开始开发自己的硅芯片!
对于安全关键的嵌入式应用,认证机构要求编译器满足“已经在使用中验证”的要求。通常需要满足某些要求(有点像“运行时间”),并通过详细的文件记录来证明。但大多数人要么无法满足这些要求,要么不想满足,因为这对于首次使用新目标/编译器的项目而言可能非常困难。
另一种方法基本上是完全不信任编译器的输出。任何编译器甚至依赖于语言的缺陷(C-90标准的附录G,有人会用吗?)都需要采用严格的静态分析、单元测试和覆盖测试以及后期的功能测试来进行覆盖。
像MISRA-C这样的标准可以帮助限制输入到编译器的C语言子集的“安全性”。另一种方法是将输入限制为语言的子集,并测试整个子集的输出。如果我们的应用程序只由该子集的组件构建,则认为已知编译器的输出内容。这通常称为“对编译器的资格认证”。
所有这些的目标都是为了能够回答QA代表的问题:“我们不仅仅依赖于编译器的确定性,而是通过以下方式来证明...”。
通过测试来验证。当你进行测试时,你既在测试你的代码,也在测试编译器。
你会发现,你或者编译器作者犯错的概率要比你用某种汇编语言编写程序时犯错的概率小得多。
有编译器验证套件可用。
我记得的一个是“Perennial”。
当我为嵌入式SOC处理器编写C编译器时,我们必须对其进行验证,并通过其他两个验证套件(名称我忘记了)进行验证。将编译器验证到这些测试套件的某个符合度水平是合同的一部分。
在为国防软件工程师设计的杂志《Crosstalk》中,你可以找到一些有关知识储备。这个问题是他们花费许多时间思考的类型。 http://www.stsc.hill.af.mil/crosstalk/2006/08/index.html (如果我能找到旧项目的笔记,我会回来的...)
还有更好的选择吗?有些人声称有。另一个选择是使用SPARK/Ada编写代码。我从未用过SPARK编写代码,但我的理解是您仍然需要将其链接到用C编写的例程上,以处理“裸机”内容。 SPARK/Ada的优点在于,您绝对可以保证任何编译器生成的代码始终相同。毫无歧义。此外,该语言允许您使用解释说明代码预期如何运行的注释进行标注。 SPARK工具集将使用这些注释来正式证明编写的代码确实执行了注释所描述的操作。所以我听说对于关键系统,SPARK/Ada是一个不错的选择。但我自己从未尝试过。
......你完全信任你的编译器
但第一次遇到编译器错误时,你会停止这样做。;-)
但最终,这就是测试的目的。对于您的测试计划来说,如何在产品中引入错误并不重要,重要的是它未能通过您的广泛测试计划。