全局變量初始化順序探究
緣起
我在上一篇文章——《調試實戰 —— 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》