如果我将一个程序编译成单个二进制文件,进行校验和,然后在同一台机器上使用相同的编译器和编译器设置重新编译它,并对重新编译的程序进行校验和校验,校验和会失败吗?
如果是这样,这是为什么?如果不是,使用不同的 CPU 会导致不同的二进制文件吗?
ric*_*ici 19
在同一台机器上用相同的设置编译相同的程序:
尽管明确的答案是“视情况而定”,但可以合理地预期大多数编译器在大多数时间都是确定性的,并且生成的二进制文件应该是相同的。事实上,一些版本控制系统依赖于此。不过,总有例外。这很可能是一些编译器的地方将决定插入一个时间戳或一些这样的(IIRC,德尔福做,例如)。或者构建过程本身可能会这样做;我已经看到 C 程序的 makefile 将预处理器宏设置为当前时间戳。(不过,我想这会算作不同的编译器设置。)
另外,请注意,如果您静态链接二进制文件,那么您就有效地合并了机器上所有相关库的状态,其中任何一个库的任何更改也会影响您的二进制文件。因此,相关的不仅仅是编译器设置。
在具有不同 CPU 的不同机器上编译相同的程序。
在这里,所有赌注都取消了。大多数现代编译器都能够进行特定于目标的优化;如果启用此选项,则二进制文件可能会有所不同,除非 CPU 相似(即使如此,也有可能)。另外,请参阅上面关于静态链接的说明:配置环境远远超出了编译器设置。除非您有非常严格的配置控制,否则两台机器之间极有可能存在差异。
-frandom-seed=123
控制一些 GCC 内部随机性。man gcc
说:
这个选项提供了一个种子,GCC 使用它代替随机数来生成某些符号名称,这些名称在每个编译文件中都必须不同。它还用于在覆盖数据文件和生成它们的目标文件中放置唯一的标记。您可以使用 -frandom-seed 选项生成可重现的相同目标文件。
__FILE__
: 把源放在一个固定的文件夹中(例如/tmp/build
)
__DATE__
, __TIME__
, __TIMESTAMP__
:
-D
-Wdate-time
or -Werror=date-time
:如果使用__TIME__
,__DATE__
或__TIMESTAMP__
are ,则警告或失败。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 基金会的资助。这不仅包括编译,还应该引起人们的兴趣。
Buildroot有一个BR2_REPRODUCIBLE
选项,可以在包级别提供一些想法,但在这一点上还远未完成。
相关主题:
您要问的是“输出确定性”。如果您编译了一次程序,立即再次编译它,您可能最终会得到相同的输出文件。然而,如果有任何变化——即使是很小的变化——尤其是在编译程序使用的组件中,那么编译器的输出也可能会发生变化。
重新编译程序是否会产生一个位对位相同的二进制文件?
对于所有编译器?不可以。至少 C# 编译器是不允许的。
Eric Lippert对为什么编译器的输出不是确定性的进行了非常彻底的分解。
[T]C# 编译器的设计永远不会产生两次相同的二进制文件。每次运行时,C# 编译器都会在每个程序集中嵌入一个新生成的 GUID,从而确保没有两个程序集完全相同。引用 CLI 规范:
Mvid 列应索引一个唯一的 GUID [...],用于标识模块的此实例。[...] 应该为每个模块新生成 Mvid [...] 虽然 [runtime] 本身不使用 Mvid,但其他工具(例如调试器 [...])依赖于以下事实: Mvid 几乎总是从一个模块到另一个模块不同。
尽管它特定于某个版本的 C# 编译器,但本文中的许多要点可以应用于任何编译器。
首先,我们假设每次总是以相同的顺序获得相同的文件列表。但这在某些情况下取决于操作系统。当您说“csc *.cs”时,操作系统提供匹配文件列表的顺序是操作系统的实现细节;编译器不会将该列表排序为规范顺序。
归档时间: |
|
查看次数: |
19007 次 |
最近记录: |