全局变量初始化顺序探究
緣起
我在上一篇文章——《調(diào)試實戰(zhàn) —— dll 加載失敗之全局變量初始化篇》中,跟大家分享了一個由于全局變量初始化順序?qū)е碌?dll 加載失敗的例子。感興趣的小伙伴兒可以點擊閱讀。
雖然我們知道了是由于全局變量初始化順序?qū)е碌膯栴},也給出了解決方案。但是有一點卻沒有刨根問底——為什么改變文件在工程文件中的順序就可以改變?nèi)肿兞砍跏蓟樞?#xff1f;是怎么影響的呢?本篇文章力求解決這個問題。
了解 Build
我們可以簡單的把整個構建過程分成三個步驟(當然實際還有其它步驟,我們一般不關心):預編譯,編譯,鏈接。
預編譯: 處理宏,#include 展開等。
編譯: 以編譯單元為單位生成對應的 .obj 文件。
鏈接: 把生成的.obj 文件和必要的文件鏈接成最后的應用程序。
猜想
因為編譯是把符號放到對應的 .obj 中,鏈接的時候才把對應的 .obj 文件鏈接成最后的應用程序。鏈接的時候應該是按照 .obj 文件出現(xiàn)的先后順序依次把 .obj 中的符號放到對應的位置。
思路
對比觀察調(diào)整順序前和調(diào)整順序后的編譯參數(shù),鏈接參數(shù)。因為猜測是鏈接導致的問題,我們主要關注鏈接參數(shù)。
編譯過程初探
當我們在 vs 中執(zhí)行 build 時的整個過程如下圖(使用 process monitor 捕獲的):
vs-msbuild-cl-link可以清晰的看到,vs 在內(nèi)部會啟動 msbuild.exe 執(zhí)行后續(xù)的操作。msbuild.exe 會間接啟動 cl.exe 進行編譯,link.exe 進行鏈接。我們還發(fā)現(xiàn)黃色高亮部分的 Tracker.exe ,這個進程主要用來加速編譯的。具體可以參考《Inside the Microsoft Build Engine Using MSBuild and Team Foundation Build》 這本書的介紹,簡單截圖如下:
filetracker-introduce簡化編譯過程
因為 vs 會通過 msbuild.exe 執(zhí)行操作,我們可以直接使用 msbuild.exe 進行構建。msbuild 有一個選項 TrackFileAccess 可以用來控制是否使用 FileTracker。為 false 時,不啟用 FileTracker。
為了簡化問題,我們直接執(zhí)行 msbuild.exe -p:TrackFileAccess=false project_file_to_build.vcxproj 。
msbuild-cl-link-param我們發(fā)現(xiàn),傳遞給 cl.exe 和 link.exe 的參數(shù)都是文件。猜測,應該是把參數(shù)保存到文件中傳遞的。據(jù)觀察,這些文件會在執(zhí)行完后被清理。得想辦法在這些文件被刪除之前保存一份,各位小伙伴兒有什么好辦法嗎?
我們先看下這些參數(shù)文件是誰創(chuàng)建和刪除的,什么時候刪除的。創(chuàng)建很簡單,肯定是 msbuild.exe。刪除呢?是 cl.exe / link.exe 還是 msbuild.exe 呢?又是什么時候刪除的呢?相信下圖能很好的回答這些問題了。
msbuild-remove-param-file我想到兩個思路:
因為這些文件是 msbuild.exe 創(chuàng)建/刪除的,可以在 msbuild.exe 中文件操作的地方加斷點。
可以暫停 cl.exe/link.exe 的執(zhí)行,拷貝我們需要的文件到桌面。
第一個思路相對來說比較復雜,今天我們嘗試第二個思路。我們該如何暫停呢?請出 gflags.exe。
中斷 link.exe
我們可以在 gflags.exe 中進行如下設置,這樣當 link.exe 啟動時就會中斷到 windbg.exe 中了。
gflags-set-debug-link斷下來后,我們可以在 windbg 中輸入 !peb 觀察參數(shù),里面包含了我們需要拷貝的文件路徑。
windbg-command-line有了文件路徑,我們就可以手動復制對應的文件到桌面慢慢研究了。
對比鏈接參數(shù)
調(diào)整 Test1.cpp Test2.cpp 在 .vcxproj 中的順序,按上面的方法分別保存?zhèn)鬟f給 link.exe 的參數(shù)文件,對比如下圖(格式有調(diào)整):
obj-link-order發(fā)現(xiàn)在兩次鏈接過程中,Test1.obj Test2.obj 出現(xiàn)的順序是不一樣的。
結論
哪個源碼文件在 .vcxproj 中先出現(xiàn),其對應的 .obj 文件在傳遞給 link.exe 的參數(shù)文件(.rsp)中越靠前,會被優(yōu)先處理。.obj 中包含的全局變量會被優(yōu)先處理。當進程啟動時,執(zhí)行全局變量初始化的時候會按照先后順序初始化。
總結
vs 內(nèi)部會使用 msbuild.exe 編譯,我們也可以直接使用 msbuild.exe 進行編譯。
使用 msbuild.exe -p:TrackFileAccess=false 可以在編譯的過程中不啟動 Tracker.exe,對我們調(diào)查問題有幫助。
我們可以在一個進程啟動時就中斷到調(diào)試器,可以使用 gflags.exe 幫我們實現(xiàn)這一點。
!peb 可以查看啟動參數(shù),環(huán)境變量等信息。
.vcxproj 中文件的順序會影響最后鏈接時的順序。
參考資料
《Inside the Microsoft Build Engine Using MSBuild and Team Foundation Build》
https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?redirectedfrom=MSDN&view=vs-2019
http://www.cppblog.com/xlshcn/archive/2007/12/07/37088.html
需要你的
總結
以上是生活随笔為你收集整理的全局变量初始化顺序探究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SiteServer CMS 新版本 V
- 下一篇: dotNET Core 3.X 依赖注入