最终目标是比较在完全相同的环境中从完全相同的源构建的 2 个二进制文件,并能够判断它们在功能上确实是等效的。
一个应用程序是将 QA 时间集中在版本之间实际更改的内容上,以及一般的更改监控。
MSVC 与 PE 格式相结合自然很难做到这一点。
到目前为止,我发现并消除了这些东西:
- PE 时间戳和校验和
- 数字签名目录条目
- 调试器部分时间戳
- PDB 签名、年龄和文件路径
- 资源时间戳
- VS_VERSION_INFO 资源中的所有文件/产品版本
- 数字签名部分
我解析 PE,找到所有这些东西的偏移量和大小,并在比较二进制文件时忽略字节范围。像魅力一样工作(好吧,对于我运行过的几个测试)。我可以说,在 Win Server 2008 上构建的 1.0.2.0 版本的签名可执行文件等于在我的 Win XP 开发盒上构建的 10.6.6.6 版本的未签名可执行文件,只要编译器版本和所有源代码和标头都相同。这似乎适用于 VC 7.1 -- 9.0。(对于发布版本)
有一个警告。
两个构建的绝对路径 必须 相同,长度必须相同。
cl.exe 将相对路径转换为绝对路径,并将它们与编译器标志等一起放入对象中。这对整个二进制文件有不成比例的影响。路径中的一个字符更改将导致一个字节在整个 .text 部分中在这里和那里多次更改(但是我怀疑有很多对象被链接)。改变路径的长度会导致明显更多的差异。在 obj 文件和链接的二进制文件中。
感觉像带有编译标志的文件路径被用作某种哈希,这使它成为链接的二进制文件,甚至影响不相关的已编译代码的放置顺序。
所以这是一个由 3 部分组成的问题(总结为“现在怎么办?”):
我是否应该放弃整个项目并回家,因为我正在尝试做的事情违反了物理定律和 MS 的公司政策?
假设我处理绝对路径问题(在策略级别或通过找到一个神奇的编译器标志),还有其他我应该注意的事情吗?(像 __TIME__这样的东西确实意味着改变了代码,所以我不介意那些没有被忽略的东西)
有没有办法强制编译器使用相对路径,或者让它认为路径不是它的样子?
最后一个原因是令人讨厌的 Windows 文件系统。你永远不知道什么时候删除几个 gigs 的源和对象以及 svn 元数据会因为恶意文件锁定而失败。至少在有空间的情况下创建新的根总是成功的。一次运行多个构建也是一个问题。运行一堆虚拟机虽然是一种解决方案,但却是一个相当繁重的解决方案。
我想知道是否有一种方法可以为一个进程及其子进程设置一个虚拟文件系统,以便多个进程树将同时看到不同的“C:\build”目录,它们只对它们私有...... -各种重量虚拟化......
更新:我们最近在GitHub 上开源了该工具。请参阅文档中的比较部分。