プログラムを単一のバイナリにコンパイルし、チェックサムを作成してから、同じマシンで同じコンパイラとコンパイラ設定で再コンパイルし、再コンパイルされたプログラムをチェックサムすると、チェックサムは失敗しますか?
もしそうなら、これはなぜですか?そうでない場合、異なるCPUを使用すると、同一でないバイナリになりますか?
プログラムを単一のバイナリにコンパイルし、チェックサムを作成してから、同じマシンで同じコンパイラとコンパイラ設定で再コンパイルし、再コンパイルされたプログラムをチェックサムすると、チェックサムは失敗しますか?
もしそうなら、これはなぜですか?そうでない場合、異なるCPUを使用すると、同一でないバイナリになりますか?
回答:
同じマシンで同じ設定で同じプログラムをコンパイルします。
最終的な答えは「依存します」ですが、ほとんどのコンパイラはほとんどの場合確定的であり、生成されるバイナリは同一であると予想するのが妥当です。実際、一部のバージョン管理システムはこれに依存しています。それでも、常に例外があります。可能性が十分にあるいくつかのコンパイラのどこかには、タイムスタンプまたはそのようないくつかの(IIRC、デルファイは、例えば、ない)を挿入することを決定します。または、ビルドプロセス自体がそれを行う場合があります。プリプロセッサマクロを現在のタイムスタンプに設定するCプログラムのメイクファイルを見てきました。(ただし、別のコンパイラ設定としてカウントされると思います。)
また、バイナリを静的にリンクすると、マシン上のすべての関連ライブラリの状態が効果的に組み込まれ、それらのいずれかの変更もバイナリに影響することに注意してください。したがって、関連するのはコンパイラの設定だけではありません。
CPUが異なる別のマシンで同じプログラムをコンパイルします。
ここでは、すべてのベットがオフになっています。最新のコンパイラのほとんどは、ターゲット固有の最適化を実行できます。このオプションが有効になっている場合、CPUが類似していない限り、バイナリは異なる可能性があります(それでも可能です)。また、静的リンクに関する上記の注意も参照してください。構成環境は、コンパイラー設定をはるかに超えています。非常に厳密な構成制御がない限り、2台のマシンで何かが異なる可能性が非常に高くなります。
gcc -c
同じである可能性がありますが、リンクされたバージョンは異なります。また、それだけでは-march
ありません。-mtune/-mcpu
and -mfpmatch
(およびおそらくその他)もあります。これらのいくつかは、インストールごとにデフォルトが異なる可能性があるため、マシンの最悪のケースを明示的に強制する必要があります。これを行うと、特にsseなしでi386に戻る場合、パフォーマンスが大幅に低下する可能性があります。そして、もちろん、CPUの1つがARMで、もう1つのCPUがi686である場合
プログラムを再コンパイルすると、ビットごとに同一のバイナリが生成されますか?
すべてのコンパイラーですか?いいえ。少なくともC#コンパイラは許可されていません。
Eric Lippertは、コンパイラの出力が決定論的ではない理由について非常に徹底的な内訳を持っています。
[T]設計上のC#コンパイラは、同じバイナリを2回生成することはありません。C#コンパイラは、実行するたびに、すべてのアセンブリに新しく生成されたGUIDを埋め込みます。これにより、2つのアセンブリがビット単位で同一になることはありません。CLI仕様から引用するには:
Mvid列は、モジュールのこのインスタンスを識別する一意のGUID [...]にインデックスを付けます。[...] Mvidはすべてのモジュールに対して新しく生成される必要があります[...] [runtime]自体はMvidを使用しませんが、他のツール(デバッガー[...]など)は、 Mvidはほとんどの場合、モジュールごとに異なります。
C#コンパイラーのバージョンに固有ですが、記事の多くのポイントはどのコンパイラーにも適用できます。
最初に、毎回同じ順序で常に同じファイルのリストを取得することを前提としています。しかし、場合によってはオペレーティングシステム次第です。「csc * .cs」と言うとき、オペレーティングシステムが一致するファイルのリストを提供する順序は、オペレーティングシステムの実装の詳細です。コンパイラはそのリストを標準的な順序に並べ替えません。
-frandom-seed=123
一部のGCC内部ランダム性を制御します。man gcc
言う:
このオプションは、すべてのコンパイル済みファイルで異なる必要がある特定のシンボル名を生成する際に、GCCが乱数の代わりに使用するシードを提供します。また、カバレッジデータファイルとそれらを生成するオブジェクトファイルに一意のスタンプを配置するためにも使用されます。-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/が取り組んでいるように、再現性と人間が読み取れるソースからのブートストラップ可能性を組み合わせると、人間が読み取れるソースによってシステムが一から決定され、それが初めてです。システムが何をしているかを知っていると信頼できます。
いいえ、100%確定的ではありません。以前、Hitachi H8プロセッサのターゲットバイナリを生成するバージョンのGCCを使用しました。
タイムスタンプの問題ではありません。タイムスタンプの問題を無視しても、特定のプロセッサアーキテクチャでは、一部のビットが1または0になる可能性のあるわずかに異なる2つの方法で同じ命令をエンコードできる場合があります。ただし、gccは同じサイズのバイナリを生成する場合がありますが、バイトの一部が1ビットのみ異なる場合、たとえば0XE0は0XE1になります。
一般的に、いいえ。最も合理的に洗練されたコンパイラは、オブジェクトモジュールにコンパイル時間を含めます。クロックをリセットする場合でも、コンパイルを開始するタイミングに関して非常に正確でなければなりません(そして、ディスクアクセスなどが以前と同じ速度であったことを望みます)。