为什么两个只有注释的程序二进制文件在gcc中没有完全匹配?

Reg*_*ser 109 c optimization gcc binary-reproducibility

我创建了两个C程序

  1. 计划1

    int main()
    {
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 计划2

    int main()
    {
    //Some Harmless comments
    }
    
    Run Code Online (Sandbox Code Playgroud)

AFAIK,在编译时,编译器(gcc)应该忽略注释和冗余空白,因此输出必须类似.

但是当我检查输出二进制文件的md5sums时,它们不匹配.我也试图与优化的编译-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
Run Code Online (Sandbox Code Playgroud)

是的,md5sums匹配多个具有相同标志的编译.

顺便说一句我的系统是gcc (GCC) 5.2.0Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

cyp*_*har 158

这是因为文件名不同(尽管字符串输出是相同的).如果您尝试修改文件本身(而不是有两个文件),您会注意到输出二进制文件不再是不同的.正如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
Run Code Online (Sandbox Code Playgroud)

这解释了为什么你的md5sums不会在构建之间发生变化,但它们在不同文件之间是不同的.如果您愿意,您可以执行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
Run Code Online (Sandbox Code Playgroud)

  • 这就是你应该比较汇编输出而不是MD5校验和的原因. (30认同)
  • 根据目标文件格式,编译时间也存储在目标文件中.因此,使用COFF文件作为示例文件a和a2将不相同. (4认同)

Jen*_*ens 27

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

试试跑步

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y
Run Code Online (Sandbox Code Playgroud)

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

  • 更不用说OP指出"md5sums匹配多个具有相同标志的编译",这表明它可能不是引起问题的时间戳.这可能是因为它们是不同的文件名. (3认同)

LSe*_*rni 15

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

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

  • strip针对二进制文件运行以删除一些脂肪.如果剥离的二进制文件是相同的,那么它是一些对程序操作不重要的元数据.
  • 生成程序集中间输出以验证差异不在实际CPU指令中(或者,更好地确定实际差异的位置)
  • 使用strings或将两个程序转储为十六进制并在两个十六进制转储上运行diff.找到差异后,您可以尝试查看是否存在一些押韵或理由(PID,时间戳,源文件时间戳......).例如,您可能有一个例程在编译时存储时间戳以用于诊断目的.

  • 您应该尝试*实际*制作两个单独的文件.我无法通过修改单个文件来重现它. (2认同)