如果我要将程序编译为单个二进制文件,请进行校验和,然后在具有相同编译器和编译器设置的同一台计算机上将其重新编译,并对重新编译的程序进行校验和,校验和会失败吗?
如果是这样,为什么呢?如果不是,使用不同的CPU是否会导致二进制文件不一致?
如果我要将程序编译为单个二进制文件,请进行校验和,然后在具有相同编译器和编译器设置的同一台计算机上将其重新编译,并对重新编译的程序进行校验和,校验和会失败吗?
如果是这样,为什么呢?如果不是,使用不同的CPU是否会导致二进制文件不一致?
Answers:
在同一台计算机上以相同设置编译相同程序:
尽管确定的答案是“取决于”,但可以合理地预期大多数编译器在大多数时间都是确定性的,并且所生成的二进制文件应该相同。实际上,某些版本控制系统依赖于此。尽管如此,总有例外。这很可能是一些编译器的地方将决定插入一个时间戳或一些这样的(IIRC,德尔福做,例如)。否则构建过程本身可能会这样做;我见过C程序的makefile文件,这些文件将预处理程序宏设置为当前时间戳。(不过,我认为这将算作是不同的编译器设置。)
另外,请注意,如果您静态链接二进制文件,则可以有效地合并计算机上所有相关库的状态,并且其中任何一个的任何更改都将影响二进制文件。因此,不仅相关的编译器设置。
在具有不同CPU的不同计算机上编译相同程序。
在这里,所有赌注都关闭了。大多数现代的编译器都能够进行针对特定目标的优化。如果启用此选项,则二进制文件可能会有所不同,除非CPU相似(即使这样,也可能)。另外,请参见上面有关静态链接的说明:配置环境远远超出了编译器设置。除非您有非常严格的配置控制,否则两台计算机之间很可能会有所不同。
gcc -c
可能完全相同,但是链接的版本不同。另外,这不只是-march
;还有-mtune/-mcpu
和-mfpmatch
(可能还有其他)。其中一些可能在不同的安装中具有不同的默认值,因此您可能需要为计算机强制使用最坏的情况。这样做可能会大大降低性能,尤其是如果您不使用sse还原到i386时。而且,当然,如果您的一个cpus是ARM,另一个是i686 ...
重新编译程序是否会产生逐位相同的二进制文件?
对于所有编译器?否。至少不允许使用C#编译器。
埃里克·利珀特(Eric Lippert)对为什么编译器的输出不确定的原因进行了详尽的分析。
根据设计,C#编译器永远不会两次生成相同的二进制文件。C#编译器在每次运行该程序集时都会在每个程序集中嵌入一个新生成的GUID,从而确保没有两个程序集是逐位相同的。引用CLI规范:
Mvid列应为标识该模块实例的唯一GUID编制索引。[...]应该为每个模块重新生成Mvid [[runtime]本身不使用Mvid,而其他工具(例如调试器[...])则依赖于以下事实:从一个模块到另一个模块,Mvid几乎总是有所不同。
尽管它特定于C#编译器的版本,但是本文中的许多要点都可以应用于任何编译器。
首先,我们假设每次总是以相同的顺序获得相同的文件列表。但这在某些情况下取决于操作系统。当您说“ csc * .cs”时,操作系统提供匹配文件列表的顺序是操作系统的实现细节;编译器不会将该列表按规范顺序排序。
-frandom-seed=123
控制一些GCC内部随机性。man gcc
说:
此选项提供了一个种子,GCC在生成某些在每个编译文件中都必须不同的符号名称时,将使用它们代替随机数。它还可用于将唯一戳记放置在coverage数据文件和生成它们的对象文件中。您可以使用-frandom-seed选项来产生可重复的相同目标文件。
__FILE__
:将源放置在固定文件夹中(例如/tmp/build
)
__DATE__
,__TIME__
,__TIMESTAMP__
:
-D
-Wdate-time
或者-Werror=date-time
:警告或任失败,如果__TIME__
,__DATE__
或者__TIMESTAMP__
是被使用。Linux内核4.4默认使用它。D
带有的标记ar
,或使用https://github.com/nh2/ar-timestamp-wiper/tree/master擦除邮票-fno-guess-branch-probability
:较旧的手册版本说这是不确定性的根源,但现在已经不行了。不知道这是否被覆盖-frandom-seed
。Debian Reproducible builds项目试图逐字节标准化Debian软件包,并且最近获得了Linux Foundation的资助。这不仅包括编译,还应引起关注。
Buildroot有一个BR2_REPRODUCIBLE
选项,可能会在软件包级别上给出一些想法,但目前还远远不够。
相关主题:
https://reproducible-builds.org/这个项目就是关于这个的,并且正在努力在尽可能多的地方为您的问题“不,它们不会有所不同”做出答案。NixOS和Debian现在其程序包的可重复性超过90%。
如果您编译一个二进制文件,而我编译一个二进制文件,并且它们一点一点相同,那么我可以放心,源代码和工具是决定输出的因素,并且您不会偷偷摸摸木马代码。
如果我们将可复制性与人类可读源的引导可扩展性结合起来(如http://bootstrappable.org/正在进行的工作),我们将获得一个由人类可读源从头开始确定的系统,只有这样,我们才能我们可以相信我们知道系统在做什么。
一般来说,没有。最合理的复杂编译器会将编译时间包括在对象模块中。即使您要重置时钟,您也必须在开始编译时非常准确(然后希望磁盘访问等与以前相同的速度)。