即使源代码和编译器标志未修改,是什么导致目标文件在编译之间发生变化?

What causes object files to vary between compilations even when the source and compiler flags are unmodified?

如果你点击它是因为你认为这是不可能的,我也是这么想的,直到我 运行 进入它。

我正在做一个用 C 语言为 PIC 编写的项目,该项目是使用 Makefile 构建的。 Makefile 非常混乱,所以我想清理它。为了确保我在执行此操作时没有破坏任何东西,我在重新创建之后记录了所有文件的哈希值:(此项目中没有子目录。使用 SDCC 和 GPUTILS 构建。)

make clean
make
md5sum ./* > ../allsums.txt

然后我修改了 Makefile 并再次尝试,这次将生成的文件与 allsums.txt.

进行比较
make clean
vim Makefile
make
md5sum -c ../allsums.txt

有趣的是,.o 文件的哈希值不匹配,但最终结果匹配。假设问题是我以某种方式造成的,我花了很多时间试图找到它。

然后,凭直觉,我使用原始 Makefile 做了这个:

make clean
make
md5sum ./* > ../allsums.txt
make clean
make
md5sum -c ../allsums.txt

我发现目标文件也在这里改变了!一些搜索让我找到 this question,它确认(至少对于 gcc).o 文件在每次编译之间发生变化。

这是什么原因造成的?

对象中的调试信息(符号、日期)可以使对象发生变化,即使代码完全相同。

为确保您没有任何零钱,只需删除对象:

strip *.o

比较 objects/performing 校验和的最佳方法是在剥离的对象上,否则你永远无法确定。

(同样的技术可以应用于可执行文件)

注意:剥离对象后,您可以 link 它们,但调试起来会很困难。复制一份即可(theobject.o不变,则):

strip theobject.o -o theobject_stripped.o

我们在交付前执行 "formal production" 可执行文件时使用该过程。

实际上我们反过来做:我们比较剥离的可执行文件,如果有差异,我们比较剥离的对象以找到罪魁祸首并缩小范围。 然后我们在源上使用我们的版本控制系统来找出它改变的原因。

编辑:如果使用自定义时间相关宏来定义目标文件中的日期 (-DDATE=\"somedate\"),则校验和过程将需要的不仅仅是一个条带手术。必须使用自定义工具从目标文件(或多个文件)中执行反向操作(删除 date/version/whatever)。 您可以受益于此功能,并通过仅将宏应用于一个包含导出符号版本 (Version.o) 的文件来保持大部分目标文件不变。

该文件的校验和会有所不同,但其他文件的校验和会相同(或者您的同事毫无意义地让您很难处理)

编辑:对于 SDCC,您有一个名为 sdobjcopy 的类似工具,其界面看起来非常非常像 objcopy,并且具有剥离对象的功能

sdobjcopy --strip-all theobject.o theobject_stripped.o

(如果 --strip-all 选项太 "violent",还有一个 --strip-debug 选项)

查看 sdobjcopy man page 了解更多详情。