編譯 UEFI 程式入門:從 uefi-simple 到第一個 .EFI

整理編譯第一個 UEFI .EFI 程式的入門思路:理解 UEFI 程式是什麼、為什麼推薦從 uefi-simple 開始、需要準備哪些工具,以及編譯和測試時常見的坑。

編譯第一個 UEFI 程式並不輕鬆:環境安裝耗時,連結器錯誤很多,.EFI 程式也不像普通桌面程式那樣有成熟直觀的編輯和執行體驗。

這篇就按入門角度整理一下:如果只是想先編譯出自己的第一個 UEFI 程式,應該從哪裡開始,哪些概念要先搞清楚,哪些坑最容易遇到。

UEFI 程式是什麼

UEFI 程式通常是一個 .EFI 檔案。

它不是 Windows 下雙擊執行的普通 .exe,而是執行在 UEFI 韌體環境裡的 PE/COFF 可執行檔。常見場景包括:

  • 啟動管理器
  • 硬體初始化工具
  • 韌體更新工具
  • 開機前診斷工具
  • 自訂啟動流程

你在系統啟動早期看到的很多功能,本質上都可能和 UEFI 應用程式、驅動或韌體服務有關。

對初學者來說,先不用急著理解完整韌體開發。第一步只要完成一個目標:編譯出一個能被 UEFI Shell 或模擬器載入的 .EFI 檔案。

為什麼不建議一開始就上 EDK II

UEFI 正式開發經常會遇到 EDK II。

EDK II 功能完整,也更接近真實韌體工程,但它對新手不太友好:

  • 工程結構複雜
  • 建構系統有學習成本
  • 環境變數和工具鏈配置比較多
  • 編譯錯誤不容易看懂
  • 很容易還沒寫程式碼,就先卡在環境上

如果目標只是「先跑起來一個最小 UEFI 程式」,更適合從輕量範例開始。

pbatard/uefi-simple 就是這類專案。它的定位很直接:提供一個簡單的 UEFI Hello World 範例,讓你先把 .EFI 編譯出來。

uefi-simple 適合用來做什麼

uefi-simple 適合做 UEFI 入門的第一塊踏板。

它主要解決三個問題:

  • 給你一個最小可編譯的 UEFI 應用結構
  • 幫你避開一開始就接觸大型韌體工程的複雜度
  • 讓你能先驗證編譯、連結、執行鏈路是否通

這個專案支援不同建構方式,包括 Visual Studio 2022 和 MinGW/gcc,也可以配合 QEMU 與 OVMF 做測試。

也就是說,你不一定非要準備真實機器反覆重開機測試。先用模擬器把程式跑起來,會安全很多。

入門前要準備哪些東西

最少需要準備幾類工具。

第一類是編譯工具鏈。

如果你在 Windows 上,可以優先考慮:

  • Visual Studio 2022
  • 或 MinGW/gcc

第二類是 UEFI 執行環境。

可以有兩種選擇:

  • 用真實機器的 UEFI Shell 執行 .EFI
  • 用 QEMU + OVMF 在虛擬環境裡測試

第三類是範例專案。

新手不建議從空目錄開始手寫建構腳本。直接使用 uefi-simple 這類最小範例,可以少踩很多建構系統的坑。

大致流程

一個最小 UEFI 程式的入門流程可以這樣理解。

第一步,取得範例專案。

1
git clone https://github.com/pbatard/uefi-simple.git

第二步,選擇建構工具鏈。

如果用 Visual Studio,就按專案裡的 Visual Studio 方案建構。
如果用 MinGW/gcc,就按專案提供的 Makefile 或說明走。

第三步,產生 .EFI 檔案。

這裡最關鍵的是確認目標架構。常見 PC 一般是 x86_64,也就是 64 位元 UEFI 環境。

第四步,把 .EFI 放到可以被 UEFI Shell 存取的位置。

如果用真實機器,通常會準備一個 FAT32 分割區或 USB 隨身碟。
如果用 QEMU,可以把目錄或映像檔掛載進去。

第五步,在 UEFI Shell 裡執行。

執行效果通常就是一個最小輸出,比如印出類似 Hello World 的內容。

最容易卡住的地方

編譯 UEFI 程式最容易卡的不是 C 語言本身,而是環境和連結。

常見問題包括:

  • 編譯器架構不對
  • 目標格式不對
  • 連結參數不完整
  • 缺少 UEFI 進入點
  • 產生的是普通可執行檔,不是 UEFI 能載入的 .EFI
  • QEMU 或 OVMF 沒配置好
  • 真實機器 Secure Boot 阻止執行未簽署程式

尤其是連結器錯誤,經常會讓新手誤以為程式碼寫錯了。
實際上,很多時候是進入函式、子系統、目標架構或連結腳本配置不對。

所以第一階段不要急著改複雜邏輯。先確保原始範例能編譯、能執行,再一點點改輸出內容。

測試時為什麼推薦 QEMU + OVMF

真實機器測試 UEFI 程式並不是不行,但新手階段不太方便。

因為你可能需要反覆:

  • 編譯
  • 複製到 USB 隨身碟
  • 重開機
  • 進入 UEFI Shell
  • 執行
  • 記錄錯誤
  • 回到系統修改

這個循環很慢。

QEMU + OVMF 的好處是可以在作業系統裡直接模擬 UEFI 環境。你可以更快地驗證 .EFI 是否能被載入,也不容易影響真實機器啟動項。

等程式基本跑通,再放到真實機器上測試,會更穩。

新手應該先改哪裡

如果你已經用範例專案編譯出了第一個 .EFI,下一步不要馬上寫複雜功能。

建議按這個順序改:

  1. 先改輸出文字,確認重新編譯後的程式確實生效。
  2. 再嘗試讀取 UEFI 提供的簡單資訊。
  3. 再理解進入函式、輸出協定和基本服務。
  4. 最後再考慮檔案系統、圖形輸出、啟動項管理等複雜功能。

這樣做的好處是每一步都能驗證。
如果一上來就改很多東西,出錯時很難判斷到底是程式碼問題、編譯問題,還是執行環境問題。

和普通 C 程式有什麼不同

UEFI 程式雖然可以用 C 寫,但它和普通 C 程式的執行環境完全不同。

普通 C 程式通常執行在作業系統裡,可以依賴標準函式庫、檔案系統、程序模型和系統呼叫。

UEFI 程式執行在作業系統啟動之前,它依賴的是 UEFI 韌體提供的服務。很多你在普通程式裡習慣使用的東西,在這裡並不是天然可用。

所以寫 UEFI 程式時,要先適應幾個變化:

  • 進入函式不一樣
  • 輸出方式不一樣
  • 可用函式庫不一樣
  • 記憶體和檔案存取方式不一樣
  • 除錯方式不一樣

這也是為什麼推薦先從最小範例開始,而不是直接照普通 C 程式的習慣寫。

一個比較現實的學習路線

如果只是入門,可以按這個路線走:

  • 第一步:編譯 uefi-simple
  • 第二步:用 QEMU + OVMF 跑起來
  • 第三步:修改 Hello World 輸出
  • 第四步:理解 UEFI Shell 怎麼載入 .EFI
  • 第五步:學習 UEFI 的進入函式和基本輸出協定
  • 第六步:再看 EDK II 或更完整的 UEFI 開發資料

這條路線的重點是先讓回饋閉環跑起來。

只要你能從原始碼產生 .EFI,再在 UEFI 環境裡看到輸出,就已經跨過了最難的第一道門檻。

參考

最後一句

編譯第一個 UEFI 程式,難點通常不在「寫出一段 C 程式碼」,而在把工具鏈、連結格式和執行環境串起來。

先別急著做複雜功能。
uefi-simple 這種最小範例開始,先得到一個能執行的 .EFI,再逐步理解 UEFI 的進入點、協定和建構方式,會輕鬆很多。

记录并分享
使用 Hugo 建立
主題 StackJimmy 設計