Hello World 是每位程式設計師的入門儀式,但其背後隱藏的編譯鏈接、執行時庫 (CRT)、作業系統載入機制卻鮮為人知。本系列透過 CRT 原始碼分析、反組譯與動態調試,層層剝開簡單程式的復雜內核。首篇聚焦 Visual Studio 編譯器選項如何決定程式入口點、字元集模式與 CRT 連結方式。
VS 專案類型 (Console / Windows) 決定兩大差異:
| 專案類型 | 預處理器宏 | 連結器 /SUBSYSTEM | 實際入口點 |
|---|---|---|---|
| Console | _CONSOLE | console | mainCRTStartup → main |
| Windows (GUI) | _WINDOWS | windows | WinMainCRTStartup → WinMain |
_CONSOLE/_WINDOWS 控制 CRT 標頭檔中的條件編譯,決定編譯哪些啟動相關原始碼Subsystem 欄位,並選擇對應的 AddressOfEntryPoint (CRT 啟動例程)crt0.c (靜態連結) 或 crtexe.c (動態連結),負責初始化後呼叫 main/WinMain現代軟體國際化要求 Unicode,但歷史包袱 (MBCS/Code Page) 仍存。VS 提供兩層控制:
| 層級 | 機制 | 影響範圍 |
|---|---|---|
| 編譯期 | 定義 _UNICODE / _MBCS 宏 | TCHAR 映射、_tmain、Windows API 寬/窄字元版本切換 |
| 連結期 | 原始碼中出現的入口函數名稱 | 決定連結哪個 CRT 啟動例程 |
int main() → 連結 mainCRTStartupint wmain() → 連結 wmainCRTStartupint WinMain() → 連結 WinMainCRTStartupint wWinMain() → 連結 wWinMainCRTStartupmain() 仍會連結窄字元版啟動例程| 模式 | 庫檔案 | 特徵 |
|---|---|---|
| 靜態連結 | libcmt.lib | CRT 代碼編譯進 EXE,體積大、無 DLL 依賴、隔離性好 |
| 動態連結 | msvcrt.lib (導入庫) → msvcrt.dll | 共享 CRT DLL,體積小、記憶體共享、但有版本地獄風險 |
本系列聚焦靜態連結 CRT (原始碼在 crt0.c 等),動態連結機制留待後續。
// 連結器輸出 PE Header 關鍵欄位
OptionalHeader.AddressOfEntryPoint = RVA(mainCRTStartup) // 由 /SUBSYSTEM 決定
OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI // Console
= IMAGE_SUBSYSTEM_WINDOWS_GUI // Windows
Windows 載入器 (ntdll!LdrpInitializeProcess) 讀取此入口點,建立主執行緒時設定 EIP/RIP,啟動 CRT。
.CRT$XCU)msvcrt.dll)exit → ExitProcess → 核心清理掌握編譯器選項與 CRT 的互動關係,是解決「為什麼我的 main 不會被呼叫」、「為什麼中文亂碼」、「為什麼 DLL 載入失敗」等經典疑難雜症的鑰匙。