C(或任何)编译器确定性性​​能

发布于 2024-07-06 05:42:17 字数 380 浏览 9 评论 0原文

在从事最近的一个项目时,一位客户 QA 代表拜访了我,他问了我一个我以前从未真正考虑过的问题:

您如何知道您使用的编译器生成的机器代码与 C 代码的功能完全匹配,并且编译器是完全确定性的?

对于这个问题我绝对没有回答,因为我一直认为编译器是理所当然的。 它接收代码并输出机器代码。 我怎样才能着手并测试编译器实际上没有添加我没有要求的功能? 或者甚至更危险地以与我期望的方式稍有不同的方式实现代码?

我知道这对每个人来说也许并不是一个真正的问题,事实上答案可能只是......“你已经超过了桶并处理它”。 然而,当在嵌入式环境中工作时,您隐式地信任您的编译器。 我如何向自己和 QA 证明我这样做是正确的?

Whilst working on a recent project, I was visited by a customer QA representitive, who asked me a question that I hadn't really considered before:

How do you know that the compiler you are using generates machine code that matches the c code's functionality exactly and that the compiler is fully deterministic?

To this question I had absolutely no reply as I have always taken the compiler for granted. It takes in code and spews out machine code. How can I go about and test that the compiler isn't actually adding functionality that I haven't asked it for? or even more dangerously implementing code in a slightly different manner to that which I expect?

I am aware that this is perhapse not really an issue for everyone, and indeed the answer might just be... "you're over a barrel and deal with it". However, when working in an embedded environment, you trust your compiler implicitly. How can I prove to myself and QA that I am right in doing so?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

天暗了我发光 2024-07-13 05:42:17

如果您担心编译器中的恶意错误,一个建议(IIRC,NSA 对某些项目的要求)是编译器二进制文件早于代码的编写。 至少这样您就知道没有人添加了针对您的程序的错误。

If your worried about malicious bugs in the compiler, one recommendation (IIRC, an NSA requirement for some projects) is that the compiler binary predate the writing of the code. At least then you know that no one has added bugs targeted at your program.

演多会厌 2024-07-13 05:42:17

如果您担心意外的机器代码不会产生可见的结果,唯一的方法可能是联系编译器供应商以获得某种能让您的客户满意的认证。

否则你就会像了解代码中的错误一样了解它 - 测试。

现代编译器的机器代码可能有很大不同,并且对于弱小的人类来说完全无法理解。

If you are concerned about unexpected machine code which doesn't produce visible results, the only way is probably to contact compiler vendor for certification of some sort which will satisfy your customer.

Otherwise you'll know it the same you know about bugs in your code - testing.

Machine code from modern compilers can be vastly different and totally incomprehensible for puny humans.

把梦留给海 2024-07-13 05:42:17

我认为可以通过某种方式将这个问题简化为停止问题

最明显的问题是,如果您使用某种程序来分析编译器及其确定性,您如何知道您的程序已正确编译并产生正确的结果?

如果您使用另一个“安全”编译器,我不确定。 我确信从头开始编写编译器可能会是一项更容易的工作。

I think it's possible to reduce this problem to the Halting Problem somehow.

The most obvious problem is that if you use some kind of program to analyze the compiler and its determinism, how do you know that your program gets compiled correctly, and produces the correct result?

If you're using another, "safe" compiler though, I'm not sure. What I'm sure is writing a compiler from scratch would probably be an easier job.

纸伞微斜 2024-07-13 05:42:17

即使是合格或经过认证的编译器也可能产生不良结果。 保持代码简单并测试、测试、测试。 或者手动遍历机器代码,同时不允许任何人为错误。 加上操作系统或您正在运行的任何环境(最好没有操作系统,只有您的程序)。

自从软件和编译器出现以来,这个问题已经在关键任务环境中得到解决。 正如许多其他做出回应的人也知道的那样。 每个行业都有自己的规则,从经过认证的编译器到编程风格(你必须始终以这种方式编程,永远不要使用这个或那个或另一个),大量的测试和同行评审。 验证每条执行路径等。

如果您不在这些行业之一,那么您就会得到您所得到的。 COTS 硬件上的 COTS 操作系统上的商业程序。 它会失败,这是一个保证。

Even a qualified or certified compiler can produce undesirable results. Keep your code simple and test, test, test. That or walk through the machine code by hand while not allowing any human error. PLus the operating system or whatever environment you are running on (preferably no operating system, just your program).

This problem has been solved in mission critical environments since software and compilers began. As many of the others who have responded also know. Each industry has its own rules from certified compilers to programming style (you must always program this way, never use this or that or the other), lots of testing and peer review. Verifying every execution path, etc.

If you are not in one of those industries, then you get what you get. A commercial program on a COTS operating system on COTS hardware. It will fail, that is a guarantee.

陪你搞怪i 2024-07-13 05:42:17

尝试单元测试。

如果这还不够,请使用不同的编译器并比较单元测试的结果。 比较 strace 输出,在虚拟机中运行测试,保留磁盘和网络 I/O 日志,然后进行比较。

或者建议编写自己的编译器并告诉他们它的成本是多少。

Try unit testing.

If that's not enough, use different compilers and compare the results of your unit tests. Compare strace outputs, run your tests in a VM, keep a log of disk and network I/O, then compare those.

Or propose to write your own compiler and tell them what it's going to cost.

画骨成沙 2024-07-13 05:42:17

您最容易证明的是您正在使用来自提供商 X 的未篡改编译器。如果他们不信任提供商 X,那就是他们的问题(如果 X 相当值得信赖)。 如果他们不信任任何编译器提供者,那么他们就完全不讲理了。

回答他们的问题:我确保通过这些方式使用来自 X 的未经篡改的编译器。 X 享有盛誉,而且我有一组很好的测试,显示我们的应用程序的行为符合预期。

其他一切都开始打开蠕虫罐。 正如罗布所说,你必须在某个地方停下来。

The most you can easily certify is that you are using an untampered compiler from provider X. If they do not trust provider X, it's their problem (if X is reasonably trustworthy). If they do not trust any compiler provider, then they are totally unreasonable.

Answering their question: I make sure I'm using an untampered compiler from X through these means. X is well reputed, plus I have a nice set of tests that show our application behaves as expected.

Everything else is starting to open the can of worms. You have to stop somewhere, as Rob says.

一抹苦笑 2024-07-13 05:42:17

有时,当您要求进行积极的优化时,您的行为确实会发生变化。

以及优化和浮点数? 忘了它!

Sometimes you do get behavioural changes when you request aggressive levels of optimisation.

And optimisation and floating point numbers? Forget it!

囍笑 2024-07-13 05:42:17

对于大多数软件开发(例如桌面应用程序),答案可能是您不知道也不关心。

在安全关键系统(例如核电站和商业航空电子设备)中,您需要关心,监管机构将要求您证明这一点。 根据我的经验,您可以通过以下两种方式之一执行此操作:

  1. 使用合格的编译器,其中“合格”意味着它已根据监管机构制定的标准进行了验证。
  2. 执行目标代码分析。 本质上,您编译一段参考代码,然后手动分析输出以证明编译器没有插入任何无法追溯到源代码的指令。

For most software development (think desktop applications) the answer is probably that you don't know and don't care.

In safety-critical systems (think nuclear power plants and commercial avionics) you do care and regulatory agencies will require you to prove it. In my experience, you can do this one of two ways:

  1. Use a qualified compiler, where "qualified" means that it has been verified according to the standards set out by the regulatory agency.
  2. Perform object code analysis. Essentially, you compile a piece of reference code and then manually analyze the output to demonstrate that the compiler has not inserted any instructions that can't be traced back to your source code.
皓月长歌 2024-07-13 05:42:17

你会得到 Dijkstra 写的那一篇。

You get the one Dijkstra wrote.

野鹿林 2024-07-13 05:42:17

选择经过正式验证的编译器,例如 Compcert C 编译器。

Select a formally verified compiler, like Compcert C compiler.

小梨窩很甜 2024-07-13 05:42:17
  1. 改变编译器的优化级别将会改变输出。
  2. 对函数的轻微更改可能会使编译器内联或不再内联函数。
  3. 对编译器的更改(例如 gcc 版本)可能会更改输出
  4. 某些库函数可能是固有的(即发出优化的程序集),而其他大多数则不是。

好消息是,对于大多数事情来说,这并不那么重要。 如果确实如此,您可能需要考虑汇编(如果它确实很重要)(例如,在 ISR 中)。

  1. Changing the optimization level of the compiler will change the output.
  2. Slight changes to a function may make the compiler inline or no longer inline a function.
  3. Changes to the compiler (gcc versions for example) may change the output
  4. Certain library functions may be instrinic (i.e., emit optimized assembly) while others most are not.

The good news is that for most things it really doesn't matter that much. Where it does, you may want to consider assembly if it really matters (e.g., in an ISR).

忘你却要生生世世 2024-07-13 05:42:17

您如何知道您使用的编译器生成的机器代码与 C 代码的功能完全匹配,并且编译器是完全确定性的?

你不需要,这就是为什么你测试生成的二进制文件,以及为什么你确保发送与你测试的相同的二进制文件。 以及为什么当您进行“较小”软件更改时,您需要进行回归测试以确保旧功能不会受到破坏。

我认证的唯一软件是航空电子设备。 FAA 认证不够严格,不足以证明软件可以正常工作,但同时它确实会迫使您跨越一定的障碍。 诀窍在于构建您的“流程”,以便尽可能提高质量,同时尽可能减少无关的跳圈。 因此,您所知道的任何东西都是毫无价值的,并且实际上不会发现错误,您可能可以避开。 任何你知道你应该做的事情,因为它发现FAA没有明确要求的错误,你最好的选择就是扭曲文字,直到听起来像是你正在给FAA/你的QA人们所要求的。

这实际上并不像我所说的那么不诚实,一般来说,联邦航空局更关心的是你是否认真负责并有信心你正在努力做好工作,而不是你到底做了什么做。

How do you know that the compiler you are using generates machine code that matches the c code's functionality exactly and that the compiler is fully deterministic?

You don't, that's why you test the resultant binary, and why you make sure to ship the same binary you tested with. And why when you make 'minor' software changes, you regression test to make sure none of the old functionality broke.

The only software I've certified is avionics. FAA certification isn't rigorous enough to prove the software works correctly, while at the same time it does force you to jump through a certain amount of hoops. The trick is to structure your 'process' so it improves quality as much as possible, with as little extraneous hoop-jumping as you can get away with. So anything that you know is worthless and won't actually find bugs, you can probably weasel out of. And anything you know you should do because it will find bugs that isn't explicitly asked for by the FAA, your best bet is to twist words until it sounds like you're giving the FAA/your QA people what they asked for.

This actually isn't as dishonest as I've made it sound, in general the FAA cares more about you being conscientious and confident that you're trying to do a good job, than about what exactly you do.

甜柠檬 2024-07-13 05:42:17

在国防软件工程师杂志《Crosstalk》中可能会找到一些知识弹药。 他们在这个问题上花费了大量醒着的时间。 http://www.stsc.hill.af.mil/ crosstalk/2006/08/index.html (如果我能从旧项目中找到我的旧笔记,我会回到这里......)

Some intellectual ammunition might be found in Crosstalk, a magazine for defense software engineers. This question is the kind of thing they spend many waking hours on. http://www.stsc.hill.af.mil/crosstalk/2006/08/index.html (If i can find my old notes from an old project, i'll be back here...)

望喜 2024-07-13 05:42:17

你永远不能完全信任编译器,即使是强烈推荐的编译器。 他们可能会发布有错误的更新,并且您的代码也会编译相同的错误。 当使用有问题的编译器更新旧代码、进行测试并发货时,这个问题会变得更加复杂,但三个月后客户却给你打电话提出问题。

这一切都回到了测试,如果说我学到了一件事的话,那就是在任何不平凡的改变之后进行彻底的测试。 如果问题似乎无法找到,请查看已编译的汇编程序并检查它是否正在执行其应该执行的操作。

我曾多次发现编译器中的错误。 有一次,存在一个错误,即 16 位变量会递增,但没有进位,并且仅当 16 位变量是头文件中定义的外部结构的一部分时。

You can never fully trust the compiler, even highly recommended ones. They could release an update that has a bug, and your code compiles the same. This problem is compounded when updating old code with the buggy compiler, doing testing and shipping out the goods only to have the customer ring you 3 months later with a problem.

It all comes back to testing, and if there is one thing I have learnt it is to thouroughly test after any non-trivial change. If the problem seems impossible to find have a look at the compiled assembler and check it's doing what it should be doing.

On several occasions I have found bugs in the compiler. One time there was a bug where 16 bit variables would get incremented but without carry and only if the 16 bit variable was part of an extern struct defined in a header file.

极致的悲 2024-07-13 05:42:17

...您隐式信任您的编译器

当你第一次遇到编译器错误时,你将停止这样做。 ;-)

但这最终就是测试的目的。 对于您的测试制度来说,错误最初如何进入您的产品并不重要,重要的是它没有通过您广泛的测试制度。

...you trust your compiler implicitly

You'll stop doing that the first time you come across a compiler bug. ;-)

But ultimately this is what testing is for. It doesn't matter to your test regime how the bug got in to your product in the first place, all that matters is that it didn't pass your extensive testing regime.

败给现实 2024-07-13 05:42:17

嗯..您不能简单地说您信任编译器的输出 - 特别是当您使用嵌入式代码时。 使用不同的编译器编译相同的代码时,不难发现生成的代码之间存在差异。 之所以会出现这种情况,是因为C标准本身过于宽松。 许多细节可以由不同的编译器以不同的方式实现,而不会违反标准。 我们如何处理这些东西? 我们尽可能避免依赖于编译器的构造。 我们可以通过选择更安全的 C 子集来处理它,例如 Misra-C 正如用户 cschol 之前提到的。 我很少需要检查编译器生成的代码,但这有时也发生在我身上。 但最终,您要依靠测试来确保代码按预期运行。

还有更好的选择吗? 有人声称有。 另一种选择是在 SPARK/Ada 中编写代码。 我从未在 SPARK 中编写过代码,但我的理解是,您仍然必须将其与用 C 编写的处理“裸机”内容的例程链接起来。 SPARK/Ada 的美妙之处在于,您绝对可以保证任何编译器生成的代码始终是相同的。 没有任何含糊之处。 最重要的是,该语言允许您对代码进行注释,并解释代码的行为方式。 SPARK 工具集将使用这些注释来正式证明所编写的代码确实执行注释所描述的操作。 所以有人告诉我,对于关键系统,SPARK/Ada 是一个不错的选择。 但我自己从未尝试过。

Well.. you can't simply say that you trust your compiler's output - particularly if you work with embedded code. It is not hard to find discrepancies between the code generated when compiling the very same code with different compilers. This is the case because the C standard itself is too loose. Many details can be implemented differently by different compilers without breaking the standard. How do we deal with this stuff? We avoid compiler dependent constructs whenever possible. We may deal with it by choosing a safer subset of C like Misra-C as previously mentioned by the user cschol. I seldom have to inspect the code generated by the compiler but that has also happened to me at times. But, ultimately, you are relying on your tests in order to make sure that the code behaves as intended.

Is there a better option out there? Some people claim that there is. The other option is to write your code in SPARK/Ada. I have never written code in SPARK but my understanding is that you would still have to link it against routines written in C that would deal with the "bare metal" stuff. The beauty of SPARK/Ada is that you are absolutely guaranteed that the code generated by any compiler is always going to be the same. No ambiguity whatsoever. On top of that, the language allows you to annotate the code with explanations as to how the code is intended to behave. The SPARK toolset will use these annotations to formally prove that the code written does indeed do what the annotations have described. So I have been told that for critical systems, SPARK/Ada is a pretty good bet. I have never tried it myself though.

计㈡愣 2024-07-13 05:42:17

您不确定编译器是否会完全按照您的预期执行。 当然,原因是编译器是一个软件,因此很容易出现错误。

编译器编写者的优势在于可以按照高质量规范进行工作,而我们其他人则必须弄清楚我们在做什么。 然而,编译器规范存在错误,以及具有微妙交互的复杂部分。 因此,弄清楚编译器应该做什么并不简单。

不过,一旦您确定了语言规范的含义,您就可以针对每个细微差别编写良好、快速的自动化测试。 这就是编译器编写相对于编写其他类型的软件具有巨大优势的地方:在测试中。 每个bug都会成为一个自动化测试用例,并且测试套件可以非常彻底。 编译器供应商在验证编译器正确性方面的预算比您多得多(您已经有一份日常工作了,对吧?)。

这对你来说意味着什么? 这意味着您需要对编译器中存在错误的可能性持开放态度,但您自己很可能找不到任何错误。

我会选择一家不太可能很快倒闭的编译器供应商,该供应商的编译器具有高质量的历史,并且已经证明了他们为产品提供服务(修补)的能力。 随着时间的推移,编译器似乎变得更加正确,所以我会选择一个已经存在了一两年左右的编译器。

集中精力让代码正确。如果代码清晰简单,那么当您确实遇到编译器错误时,您将不会遇到认真思考才能确定问题出在哪里。 编写良好的单元测试,这将确保您的代码执行您期望的操作。

You don't know for sure that the compiler will do exactly what you expect. The reason is, of course, that a compiler is a peice of software, and is therefore susceptible to bugs.

Compiler writers have the advantage of working from a high quality spec, while the rest of us have to figure out what we're making as we go along. However, compiler specs also have bugs, and complex parts with subtle interactions. So, it's not exactly trivial to figure out what the compiler should be doing.

Still, once you decide what you think the language spec means, you can write a good, fast, automated test for every nuance. This is where compiler writing has a huge advantage over writing other kinds of software: in testing. Every bug becomes an automated test case, and the test suite can very thorough. Compiler vendors have a lot more budget to invest in verifying the correctness of the compiler than you do (you already have a day job, right?).

What does this mean for you? It means that you need to be open to the possibilities of bugs in your compiler, but chances are you won't find any yourself.

I would pick a compiler vendor that is not likely to go out of business any time soon, that has a history of high quality in their compilers, and that has demonstrated their ability to service (patch) their products. Compilers seem to get more correct over time, so I'd choose one that's been around a decade or two.

Focus your attention on getting your code right. If it's clear and simple, then when you do hit a compiler bug, you won't have to think really hard to decide where the problem lies. Write good unit tests, which will ensure that your code does what you expect it to do.

陈甜 2024-07-13 05:42:17

对于安全关键的嵌入式应用程序,认证机构要求编译器满足“经过使用验证”的要求。 通常需要满足某些要求(类似于“运行时间”)并通过详细文档进行证明。 然而,大多数人要么不能要么不想满足这些要求,因为这可能非常困难,特别是在您的第一个使用新目标/编译器的项目中。

另一种方法基本上是根本不信任编译器的输出。 任何编译器甚至语言相关(C-90 标准的附录 G,有人吗?)的缺陷都需要通过一组严格的静态分析、单元测试和覆盖测试以及后续的功能测试来弥补。

MISRA-C 这样的标准可以帮助限制编译器的输入C 语言的“安全”子集。 另一种方法是将编译器的输入限制为语言的子集,并测试整个子集的输出是什么。 如果我们的应用程序仅由子集中的组件构建,则假定已知编译器的输出是什么。 通常是“编译器的资格”。

所有这一切的目标是能够回答 QA 代表的问题“我们不仅仅依赖于编译器的确定性,而是我们证明它的方式......”。

For safety critical embedded application certifying agencies require to satisfy the "proven-in-use" requirement for the compiler. There are typically certain requirements (kind of like "hours of operation") that need to be met and proven by detailed documentation. However, most people either cannot or don't want to meet these requirements because it can be very difficult especially on your first project with a new target/compiler.

One other approach is basically to NOT trust the compiler's output at all. Any compiler and even language-dependent (Appendix G of the C-90 standard, anyone?) deficiencies need to be covered by a strict set of static analysis, unit- and coverage testing in addition to the later functional testing.

A standard like MISRA-C can help to restrict the input to the compiler to a "safe" subset of the C language. Another approach is to restrict the input to a compiler to a subset of a language and test what the output for the entire subset is. If our application is only built of components from the subset it is assumed to be known what the output of the compiler will be. The usually goes by "qualification of the compiler".

The goal of all of this is to be able to answer the QA representative's question with "We don't just rely on determinism of the compiler but this is the way we prove it...".

窗影残 2024-07-13 05:42:17

通过测试你就知道了。 当您测试时,您正在测试代码和编译器。

您会发现您或编译器编写者犯错误的可能性比用某种汇编语言编写相关程序时犯错误的可能性要小得多。

You know by testing. When you test, you're testing your both code and the compiler.

You will find that the odds that you or the compiler writer have made an error are much smaller than the odds that you would make an error if you wrote the program in question in some assembly language.

葵雨 2024-07-13 05:42:17

有可用的编译器验证套件。
我记得的是“Perennial”

当我为嵌入式 SOC 处理器开发 C 编译器时,我们必须根据此套件和其他两个验证套件(我忘记了名称)来验证编译器。 验证编译器在一定程度上符合这些测试套件是合同的一部分。

There are compiler validation suits available.
The one I remember is "Perennial".

When I worked on a C compiler for a embedded SOC processor we had to validate the compiler against this and two other validation suits (that I forget the name of). Validating the compiler to a certain level of conformance to these test suits was part of the contract.

向地狱狂奔 2024-07-13 05:42:17

这一切都归结为信任。 您的客户信任任何编译器吗? 使用它,或者至少比较你和他们的输出代码。

如果他们不信任任何语言,是否有该语言的参考实现? 你能说服他们相信它吗? 然后将您的与参考进行比较或使用参考。

这一切都假设您实际上验证了从供应商/提供商处获得的实际代码,并且检查编译器没有被篡改,这应该是第一步。

无论如何,这仍然留下了一个问题:在没有引用的情况下,如何从头开始验证编译器。 这看起来确实需要大量工作,并且需要语言的定义,而该定义并不总是可用,有时定义是编译器。

It all boils down to trust. Does your customer trust any compiler? Use that, or at least compare output code between yours and theirs.

If they don't trust any, is there a reference implementation for the language? Could you convince them to trust it? Then compare yours against the reference or use the reference.

This all assuming you actually verify the actual code you get from the vendor/provider and that you check the compiler has not been tampered with, which should be the first step.

Anyhow this still leaves the question about how would you verify, without having references, a compiler, from scratch. That certainly looks like a ton of work and requires a definition of the language, which not always is available, sometimes the definition is the compiler.

南城追梦 2024-07-13 05:42:17

您可以在任何级别应用该论点:您信任第三方库吗? 你信任操作系统吗? 你相信处理器吗?

当然,为什么这可能是一个有效的担忧的一个很好的例子是 Ken Thompson 如何将后门放入原始的“登录”程序中......并修改了 C 编译器,以便即使您重新编译登录,您仍然可以获得后门。 请参阅此帖子了解更多信息细节。

关于加密算法也提出了类似的问题——我们如何知道 DES 中不存在可供 NSA 窥探的后门?

最后,您必须决定您是否足够信任您正在构建的基础设施而无需担心它,否则您必须开始开发自己的硅芯片!

You can apply that argument at any level: do you trust the third party libraries? do you trust the OS? do you trust the processor?

A good example of why this may be a valid concern of course, is how Ken Thompson put a backdoor into the original 'login' program ... and modified the C compiler so that even if you recompiled login you still got the backdoor. See this posting for more details.

Similar questions have been raised about encryption algorithms -- how do we know there isn't a backdoor in DES for the NSA to snoop through?

At the end of the you have to decide if you trust the infrastructure you are building on enough to not worry about it, otherwise you have to start developing your own silicon chips!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文