【问题标题】:What causes object files to vary between compilations even when the source and compiler flags are unmodified?即使源和编译器标志未修改,是什么导致目标文件在编译之间发生变化?
【发布时间】:2016-09-23 18:58:11
【问题描述】:

如果您因为认为这不可能而点击它,那么在遇到它之前我也是这么想的。

我正在做一个项目,用 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 文件在每次编译之间都会发生变化。

是什么原因造成的?

【问题讨论】:

  • 不,gcc 将创建相同的文件(即匹配),除非构建更改了某些内容。如果源文件有类似 (char build_time[] = DATE) 的东西,其中有一个编译参数 -DDATE="`date`",那可能会改变事情。
  • 理解/解决此问题的更好方法是获取每个文件的十六进制转储(使用odxxd)和diff 以查看哪些部分发生了变化。然后,您可以向后工作以查看给定 .o 部分中的哪个偏移量发生了变化。然后,将其与特定符号相关联。然后,在.h.c 文件中查找符号的定义。另外,长度一样吗?
  • 将特定于构建的信息嵌入到对象和可执行文件中并不少见。例如,PE 头包含一个由链接器填充的时间戳字段(PE 是 Windows 上的可执行文件和 DLL 格式)。

标签: c gcc makefile pic sdcc


【解决方案1】:

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

为确保您没有任何更改,只需剥离对象:

strip *.o

比较对象/对它们执行校验和的最佳方法是剥离对象,否则您永远无法确定。

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

注意:一旦您剥离了对象,您就可以链接它们,但您将很难进行调试。你可以在副本上做(theobject.o 不变,那么):

strip theobject.o -o theobject_stripped.o

我们在交付前执行可执行文件的“正式生产”时使用该流程。

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

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

该文件上的校验和会有所不同,但其他的会相同(或者您的同事无意义地让您变得非常困难)

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

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

(如果--strip-all 选项太“暴力”,还有一个--strip-debug 选项)

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

【讨论】:

  • 您已经确定了差异的可能原因,但剥离目标文件并不能确定是否将其清理干净,因为不同的部分可能存在于实际的程序数据中。例如,可能有包含构建时间戳的启动横幅或内部版本字符串。
  • @JohnBollinger:你是对的,但在这种情况下,它是一个自定义过程,可以使用自定义工具还原,例如剥离+查找版本/日期区域并将其空白。这个自定义过程应该在 makefile 中很容易识别。
  • 搜索的关键阶段是“可重现的构建”,它们是从安全角度来看的,但问题是一样的。
  • strip 似乎是一个很棒的工具,但它似乎只适用于由 GNU 工具制作的目标文件。 (我的 PIC 项目使用 SDCC 和 GPUTILS。)
  • @EchoLynx 为您找到了一些东西,请参阅我的最后编辑 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 2019-10-31
  • 1970-01-01
  • 2012-01-15
  • 2021-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多