web-dev-qa-db-ja.com

コメントのみが変更されたプログラムの2つのバイナリがgccで完全に一致しないのはなぜですか?

2つのCプログラムを作成しました

  1. プログラム1

    _int main()
    {
    }
    _
  2. プログラム2

    _int main()
    {
    //Some Harmless comments
    }
    _

私の知る限り、コンパイル時に、compiler(gcc)はコメントと冗長なホワイトペースを無視する必要があります。したがって、出力は同様でなければなりません。

しかし、出力バイナリのmd5sumをチェックしたとき、それらは一致しません。また、最適化_-O3_および_-Ofast_を使用してコンパイルしようとしましたが、まだ一致しませんでした。

ここで何が起きてるの?

編集:正確なコマンドとそこにmd5sumsがあります(t1.cはプログラム1であり、t2.cはプログラム2です)

_gcc ./t1.c -o aaa
gcc ./t2.c -o bbb
98c1a86e593fd0181383662e68bac22f  aaa
c10293cbe6031b13dc6244d01b4d2793  bbb

gcc ./t2.c -Ofast -o bbb
gcc ./t1.c -Ofast -o aaa
2f65a6d5bc9bf1351bdd6919a766fa10  aaa
c0bee139c47183ce62e10c3dbc13c614  bbb


gcc ./t1.c -O3 -o aaa
gcc ./t2.c -O3 -o bbb
564a39d982710b0070bb9349bfc0e2cd  aaa
ad89b15e73b26e32026fd0f1dc152cd2  bbb
_

そして、はい、md5sumsは同じフラグを持つ複数のコンパイルで一致します。

ところで私のシステムはgcc (GCC) 5.2.0と_Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux_です

109
Registered User

ファイル名が異なるためです(ただし、文字列の出力は同じです)。 (2つのファイルを持つのではなく)ファイル自体を変更しようとすると、出力バイナリがもはや変わらないことがわかります。 Jensと私が言ったように、それはGCCが作成したバイナリにメタデータの全負荷をダンプするためです。 正確なソースファイル名を含む (およびAFAICSはclangも同様)。

これを試して:

$ cp code.c code2.c subdir/code.c
$ gcc code.c -o a
$ gcc code2.c -o b
$ gcc subdir/code.c -o a2
$ diff a b
Binary files a and b differ
$ diff a2 b
Binary files a2 and b differ
$ diff -s a a2
Files a and a2 are identical

これは、ビルド間でmd5sumが変わらないのに、ファイルごとに異なる理由を説明しています。必要に応じて、Jensが提案したことを実行し、各バイナリのstringsの出力を比較して、ファイル名がバイナリに埋め込まれていることがわかります。これを「修正」したい場合は、バイナリをstripできます。メタデータは削除されます:

$ strip a a2 b
$ diff -s a b
Files a and b are identical
$ diff -s a2 b
Files a2 and b are identical
$ diff -s a a2
Files a and a2 are identical
158
cyphar

最も一般的な理由は、コンパイラによって追加されたファイル名とタイムスタンプです(通常はELFセクションのデバッグ情報部分にあります)。

実行してみてください

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y

理由がわかるかもしれません。私はかつてこれを使用して、同じソースが異なるディレクトリでコンパイルされたときに異なるコードを引き起こす理由を見つけました。発見は、__FILE__マクロはabsoluteファイル名に展開され、両方のツリーで異なります。

27
Jens

ソースファイル名がストリップされていないバイナリになることに注意してください、そのため、異なる名前のソースファイルからの2つのプログラムは異なるハッシュを持ちます。

同様の状況で、上記が当てはまらない場合、試すことができます:

  • バイナリに対してstripを実行して、脂肪を除去します。除去されたバイナリが同じ場合、プログラム操作に不可欠ではないメタデータがいくつかありました。
  • アセンブリ中間出力を生成して、違いが実際のCPU命令にないことを確認します(または、違いが実際にどこにあるのかを正確に特定するためにis
  • stringsを使用するか、両方のプログラムを16進数にダンプし、2つの16進数ダンプで差分を実行します。違いを見つけたら、韻や理由(PID、タイムスタンプ、ソースファイルのタイムスタンプなど)があるかどうかを確認します。たとえば、診断のためにルーチン コンパイル時にタイムスタンプを保存する を使用できます。
15
LSerni