首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

Hello World 程式背後的故事(二):從進程建立到 CRT 入口的完整旅程

     編輯:關於C

承接上篇「編譯器選項與 CRT」,本文聚焦 Windows 下進程建立後、主線程啟動、CRT 初始化至 main/WinMain 入口的完整調用鏈。

1. 進程建立 ≠ 代碼執行

CreateProcess 成功僅意味著:

  • 資源分配(句柄表、虛擬地址空間)
  • PE 映像載入
  • PEB 初始化、全域堆建立、DLL 載入

進程本身是「惰性」容器,不主動執行代碼。必須建立主線程才能開始指令流。

2. 線程建立的內核路徑

CreateThread / CreateRemoteThread
      ↓
ntdll!NtCreateThreadEx (syscall 0x58, sysenter)
      ↓
內核:建立 ETHREAD、KTHREAD、TEB
      ↓
設置線程初始 EIP = ntdll!RtlUserThreadStart

NtCreateThreadEx 為非文檔 API,參數原型網上版本不一,微軟不保證穩定性。

3. 用戶態啟動樁:RtlUserThreadStart

  • 建立 SEH 異常處理鏈(Fs:[0])。
  • 從內核傳遞的兩個參數中取出:
        • 用戶指定的線程入口函數
        • 入口參數
  • 調用 kernel32!BaseThreadInitThunk。

注意:這兩個參數非由用戶代碼壓棧,而是內核在建立線程上下文時硬寫入的。

4. BaseThreadInitThunk:最後一躍

  • 整理參數棧幀。
  • 調用用戶線程入口(對主線程,此入口非 main,而是 PE 標頭指定的 AddressOfEntryPoint)。

5. VC++ 專屬:mainCRTStartup

VC 編譯的可執行檔,PE 入口點為 mainCRTStartup(或 wmainCRTStartup/WinMainCRTStartup),其職責:

  1. CRT 全域初始化(_initterm 執行 _XI_* 區段構造函數)。
  2. 命令行解析:__getmainargs 填充 argc/argv/envp。
  3. 呼叫用戶 main/wmain/WinMain。
  4. 收集返回值,執行 _onexit/atexit 登記函數。
  5. ExitProcess 終止進程。

完整調用鏈圖解

CreateProcess
  → 內核建立進程對象
  → 內核建立主線程 (EIP=RtlUserThreadStart)
    → RtlUserThreadStart (建立 SEH)
      → BaseThreadInitThunk (整理參數)
        → mainCRTStartup (PE 入口點)
          → CRT 初始化 (_initterm)
          → __getmainargs (解析命令行)
          → main()  ← 使用者代碼從這裡開始
          → 退出處理 (_onexit)
          → ExitProcess

核心洞察

  • 「程式從 main 開始」是 CRT 精心包裝的假象。
  • 全域物件構造在 main 之前 執行(_initterm 階段),異常將導致進程啟動失敗。
  • 理解此鏈路是調試啟動崩潰、分析殼保護、編寫無 CRT 程式的前提。

下一篇將深入 CRT 初始化細節與 __getmainargs 實現。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved