为什么只有注释更改的两个二进制程序在 gcc 中不完全匹配?
Why don't two binaries of programs with only comments changed exactly match in gcc?
我创建了两个 C 程序
节目 1
int main()
{
}
节目 2
int main()
{
//Some Harmless comments
}
据我所知,在编译时,编译器 (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
是的,md5sums 在具有相同标志的多个编译中匹配。
顺便说一句,我的系统是 gcc (GCC) 5.2.0
和 Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
最常见的原因是编译器添加的文件名和时间戳(通常在 ELF 部分的调试信息部分)。
尝试运行
$ strings -a program > x
...recompile program...
$ strings -a program > y
$ diff x y
你可能会明白原因。我曾经用它来找出为什么同一个源代码在不同的目录下编译时会产生不同的代码。结果是 __FILE__
宏扩展为 绝对 文件名,在两棵树中都不同。
注意:请记住,源文件名进入未剥离的二进制文件,因此来自不同名称源文件的两个程序将具有不同的哈希值。
类似的情况,如果上述不适用,可以试试:
- 运行ning
strip
对二进制文件去掉一些胖子。如果剥离的二进制文件是相同的,那么它是一些对程序操作不重要的元数据。
- 生成汇编中间输出以验证差异不在实际 CPU 指令中(或者,但是,为了更好地查明差异实际上 是 )
- 使用
strings
,或将两个程序都转储到十六进制,并 运行 对两个十六进制转储进行比较。一旦找到差异,您可能会尝试查看它们是否有一些韵律或原因(PID、时间戳、源文件时间戳...)。例如,您可能有一个例程 storing the timestamp at compile time 用于诊断目的。
因为文件名不同(虽然输出的字符串是一样的)。如果您尝试修改文件本身(而不是拥有两个文件),您会注意到输出二进制文件不再不同。正如 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
这解释了为什么您的 md5sums 在构建之间没有变化,但它们在不同文件之间是不同的。如果需要,您可以按照 Jens 的建议进行操作,并比较每个二进制文件的 strings
的输出,您会注意到文件名嵌入在二进制文件中。如果你想 "fix" 这个,你可以 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
我创建了两个 C 程序
节目 1
int main() { }
节目 2
int main() { //Some Harmless comments }
据我所知,在编译时,编译器 (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
是的,md5sums 在具有相同标志的多个编译中匹配。
顺便说一句,我的系统是 gcc (GCC) 5.2.0
和 Linux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
最常见的原因是编译器添加的文件名和时间戳(通常在 ELF 部分的调试信息部分)。
尝试运行
$ strings -a program > x
...recompile program...
$ strings -a program > y
$ diff x y
你可能会明白原因。我曾经用它来找出为什么同一个源代码在不同的目录下编译时会产生不同的代码。结果是 __FILE__
宏扩展为 绝对 文件名,在两棵树中都不同。
注意:请记住,源文件名进入未剥离的二进制文件,因此来自不同名称源文件的两个程序将具有不同的哈希值。
类似的情况,如果上述不适用,可以试试:
- 运行ning
strip
对二进制文件去掉一些胖子。如果剥离的二进制文件是相同的,那么它是一些对程序操作不重要的元数据。 - 生成汇编中间输出以验证差异不在实际 CPU 指令中(或者,但是,为了更好地查明差异实际上 是 )
- 使用
strings
,或将两个程序都转储到十六进制,并 运行 对两个十六进制转储进行比较。一旦找到差异,您可能会尝试查看它们是否有一些韵律或原因(PID、时间戳、源文件时间戳...)。例如,您可能有一个例程 storing the timestamp at compile time 用于诊断目的。
因为文件名不同(虽然输出的字符串是一样的)。如果您尝试修改文件本身(而不是拥有两个文件),您会注意到输出二进制文件不再不同。正如 Jens 和我所说,这是因为 GCC 将整个负载的元数据转储到它构建的二进制文件中,
试试这个:
$ 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
这解释了为什么您的 md5sums 在构建之间没有变化,但它们在不同文件之间是不同的。如果需要,您可以按照 Jens 的建议进行操作,并比较每个二进制文件的 strings
的输出,您会注意到文件名嵌入在二进制文件中。如果你想 "fix" 这个,你可以 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