缘起
我在上一篇文章——《调试实战 | dll 加载失败之全局变量初始化篇》中,跟大家分享了一个由于全局变量初始化顺序导致的 dll
加载失败的例子。感兴趣的小伙伴儿可以点击阅读。
虽然我们知道了是由于全局变量初始化顺序导致的问题,也给出了解决方案。但是有一点却没有刨根问底——为什么改变文件在工程文件中的顺序就可以改变全局变量初始化顺序?是怎么影响的呢?本篇文章力求解决这个问题。
了解 vs 编译
我们可以简单的把整个构建过程分成三个步骤(当然实际还有其它步骤,我们一般不关心):预编译,编译,链接。
预编译: 处理宏,#include
展开等。
编译: 以编译单元为单位生成对应的 .obj
文件。
链接: 把生成的.obj
文件和必要的文件链接成最后的应用程序。
猜想
因为编译是把符号放到对应的 .obj
中,链接的时候才把对应的 .obj
文件链接成最后的应用程序。链接的时候应该是按照 .obj
文件出现的先后顺序依次把 .obj
中的符号放到对应的位置。
思路
对比观察调整顺序前和调整顺序后的编译参数,链接参数。因为猜测是链接导致的问题,我们主要关注链接参数。
编译过程初探
当我们在 vs
中执行 build
时的整个过程如下图(使用 process monitor
捕获的):
可以清晰的看到,vs
在内部会启动 msbuild.exe
执行后续的操作。msbuild.exe
会间接启动 cl.exe
进行编译,link.exe
进行链接。我们还发现黄色高亮部分的 Tracker.exe
,这个进程主要用来加速编译的。具体可以参考《Inside the Microsoft Build Engine Using MSBuild and Team Foundation Build》
这本书的介绍,简单截图如下:
简化编译过程
因为 vs
会通过 msbuild.exe
执行操作,我们可以直接使用 msbuild.exe
进行构建。msbuild
有一个选项 TrackFileAccess
可以用来控制是否使用 FileTracker
。为 false
时,不启用 FileTracker
。
为了简化问题,我们直接执行 msbuild.exe -p:TrackFileAccess=false project_file_to_build.vcxproj
。
我们发现,传递给 cl.exe
和 link.exe
的参数都是文件。猜测,应该是把参数保存到文件中传递的。据观察,这些文件会在执行完后被清理。得想办法在这些文件被删除之前保存一份,各位小伙伴儿有什么好办法吗?
我们先看下这些参数文件是谁创建和删除的,什么时候删除的。创建很简单,肯定是 msbuild.exe
。删除呢?是 cl.exe / link.exe
还是 msbuild.exe
呢?又是什么时候删除的呢?相信下图能很好的回答这些问题了。
我想到两个思路:
- 因为这些文件是
msbuild.exe
创建/删除的,可以在msbuild.exe
中文件操作的地方加断点。 - 可以暂停
cl.exe/link.exe
的执行,拷贝我们需要的文件到桌面。
第一个思路相对来说比较复杂,今天我们尝试第二个思路。我们该如何暂停呢?请出 gflags.exe
。
中断 link.exe
我们可以在 gflags.exe
中进行如下设置,这样当 link.exe
启动时就会中断到 windbg.exe
中了。
断下来后,我们可以在 windbg
中输入 !peb
观察参数,里面包含了我们需要拷贝的文件路径。
有了文件路径,我们就可以手动复制对应的文件到桌面慢慢研究了。
对比链接参数
调整 Test1.cpp Test2.cpp
在 .vcxproj
中的顺序,按上面的方法分别保存传递给 link.exe
的参数文件,对比如下图(格式有调整):
发现在两次链接过程中,Test1.obj Test2.obj
出现的顺序是不一样的。
结论
哪个源码文件在 .vcxproj
中先出现,其对应的 .obj
文件在传递给 link.exe
的参数文件(.rsp
)中越靠前,会被优先处理。.obj
中包含的全局变量会被优先处理。当进程启动时,执行全局变量初始化的时候会按照先后顺序初始化。
总结
vs
内部会使用msbuild.exe
编译,我们也可以直接使用msbuild.exe
进行编译。- 使用
msbuild.exe -p:TrackFileAccess=false
可以在编译的过程中不启动Tracker.exe
,对我们调查问题有帮助。 - 我们可以在一个进程启动时就中断到调试器,可以使用
gflags.exe
帮我们实现这一点。 !peb
可以查看启动参数,环境变量等信息。.vcxproj
中文件的顺序会影响最后链接时的顺序。
参考资料
《Inside the Microsoft Build Engine Using MSBuild and Team Foundation Build》