112

我创建了两个 C 程序

  1. 程序 1

    int main()
    {
    }
    
  2. 节目二

    int main()
    {
    //Some Harmless comments
    }
    

AFAIK,编译时,编译器(gcc)应该忽略注释和多余的空白,因此输出必须相似。

但是当我检查输出二进制文件的 md5sum 时,它们不匹配。我也尝试使用优化进行编译,-O3-Ofast它们仍然不匹配。

这里发生了什么?

编辑:确切的命令和 md5sum 是(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

4

3 回答 3

160

这是因为文件名不同(尽管字符串输出相同)。如果您尝试修改文件本身(而不是拥有两个文件),您会注意到输出二进制文件不再不同。正如 Jens 和我所说,这是因为 GCC 将整个加载的元数据转储到它构建的二进制文件中,包括确切的源文件名(AFAICS 也是如此)。

试试这个:

$ 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
于 2015-09-04T15:10:05.353 回答
28

最常见的原因是编译器添加的文件名和时间戳(通常在 ELF 部分的调试信息部分)。

尝试运行

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

你可能会看到原因。我曾经用它来找出为什么相同的源代码在不同的目录中编译时会导致不同的代码。发现是__FILE__宏扩展为绝对文件名,在两棵树中都不同。

于 2015-09-04T14:57:03.173 回答
16

注意:请记住源文件名进入未剥离的二进制文件,因此来自不同名称源文件的两个程序将具有不同的哈希值。

在类似情况下,如果上述情况不适用,您可以尝试:

  • strip对二进制文件运行以删除一些脂肪。如果剥离的二进制文件相同,那么它是一些对程序操作不是必需的元数据。
  • 生成程序集中间输出以验证差异不在实际的 CPU 指令中(或者,为了更好地确定差异实际在哪里
  • 使用strings,或将两个程序转储到十六进制并在两个十六进制转储上运行差异。一旦找到差异,您可能会尝试查看它们是否有一些押韵或原因(PID,时间戳,源文件时间戳......)。例如,您可能有一个例程在编译时存储时间戳以用于诊断目的。
于 2015-09-04T15:07:43.797 回答