最终目标是比较两个从完全相同的源代码在完全相同环境下编译出的二进制文件,并且能够确认它们功能上确实等价。
其中一个应用就是能够将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文件还是在链接的二进制文件中。
感觉像是将带有编译标志的文件路径用作某种哈希值,使其成为链接二进制文件甚至影响编译代码片段的放置顺序。
因此,这里有一个分为三个部分的问题(概括为“现在怎么办?”):
如果我试图做的事情违背了物理定律和微软公司政策,那我应该放弃整个项目回家吗?
假设我解决了绝对路径问题(在政策层面或通过找到一个神奇的编译器标志),还有其他需要注意的事项吗?(例如__TIME__确实表示代码已更改,所以不介意这些内容没有被忽略)
有没有一种方法可以强制编译器使用相对路径,或者欺骗编译器认为路径不是它所看到的那样?
最后一项的原因是Windows文件系统非常烦人。你永远不知道删除数GB的源文件、目标文件和SVN元数据是否会因为某个流浪文件锁而失败。至少,在还有剩余空间的情况下,创建新根总是成功的。同时运行多个构建也是一个问题。虽然运行一堆虚拟机是一个解决方案,但它比较重量级。
我想知道是否有一种方法可以为进程及其子进程设置虚拟文件系统,以便几个进程树同时看到仅对它们私有的不同的"C:\build"目录...一种轻量级的虚拟化解决方案...
更新:我们最近在GitHub上开源了这个工具。请查看文档中的比较部分。
/PDBALTPATH:%_PDB%
(这会导致实际路径从二进制映像中删除)后,重新构建VC++ 2015项目,则peparse将其报告为与原始构建“不等效”。 - dxiv