<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>C 語言 on KnightLi的博客</title>
        <link>https://www.knightli.com/zh-tw/tags/c-%E8%AA%9E%E8%A8%80/</link>
        <description>Recent content in C 語言 on KnightLi的博客</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-tw</language>
        <lastBuildDate>Thu, 30 Apr 2026 19:53:08 +0800</lastBuildDate><atom:link href="https://www.knightli.com/zh-tw/tags/c-%E8%AA%9E%E8%A8%80/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>編譯 UEFI 程式入門：從 uefi-simple 到第一個 .EFI</title>
        <link>https://www.knightli.com/zh-tw/2026/04/30/compile-uefi-program-beginner-guide/</link>
        <pubDate>Thu, 30 Apr 2026 19:53:08 +0800</pubDate>
        
        <guid>https://www.knightli.com/zh-tw/2026/04/30/compile-uefi-program-beginner-guide/</guid>
        <description>&lt;p&gt;編譯第一個 UEFI 程式並不輕鬆：環境安裝耗時，連結器錯誤很多，&lt;code&gt;.EFI&lt;/code&gt; 程式也不像普通桌面程式那樣有成熟直觀的編輯和執行體驗。&lt;/p&gt;
&lt;p&gt;這篇就按入門角度整理一下：如果只是想先編譯出自己的第一個 UEFI 程式，應該從哪裡開始，哪些概念要先搞清楚，哪些坑最容易遇到。&lt;/p&gt;
&lt;h2 id=&#34;uefi-程式是什麼&#34;&gt;UEFI 程式是什麼
&lt;/h2&gt;&lt;p&gt;UEFI 程式通常是一個 &lt;code&gt;.EFI&lt;/code&gt; 檔案。&lt;/p&gt;
&lt;p&gt;它不是 Windows 下雙擊執行的普通 &lt;code&gt;.exe&lt;/code&gt;，而是執行在 UEFI 韌體環境裡的 PE/COFF 可執行檔。常見場景包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;啟動管理器&lt;/li&gt;
&lt;li&gt;硬體初始化工具&lt;/li&gt;
&lt;li&gt;韌體更新工具&lt;/li&gt;
&lt;li&gt;開機前診斷工具&lt;/li&gt;
&lt;li&gt;自訂啟動流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你在系統啟動早期看到的很多功能，本質上都可能和 UEFI 應用程式、驅動或韌體服務有關。&lt;/p&gt;
&lt;p&gt;對初學者來說，先不用急著理解完整韌體開發。第一步只要完成一個目標：編譯出一個能被 UEFI Shell 或模擬器載入的 &lt;code&gt;.EFI&lt;/code&gt; 檔案。&lt;/p&gt;
&lt;h2 id=&#34;為什麼不建議一開始就上-edk-ii&#34;&gt;為什麼不建議一開始就上 EDK II
&lt;/h2&gt;&lt;p&gt;UEFI 正式開發經常會遇到 EDK II。&lt;/p&gt;
&lt;p&gt;EDK II 功能完整，也更接近真實韌體工程，但它對新手不太友好：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工程結構複雜&lt;/li&gt;
&lt;li&gt;建構系統有學習成本&lt;/li&gt;
&lt;li&gt;環境變數和工具鏈配置比較多&lt;/li&gt;
&lt;li&gt;編譯錯誤不容易看懂&lt;/li&gt;
&lt;li&gt;很容易還沒寫程式碼，就先卡在環境上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果目標只是「先跑起來一個最小 UEFI 程式」，更適合從輕量範例開始。&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/pbatard/uefi-simple&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;pbatard/uefi-simple&lt;/a&gt; 就是這類專案。它的定位很直接：提供一個簡單的 UEFI Hello World 範例，讓你先把 &lt;code&gt;.EFI&lt;/code&gt; 編譯出來。&lt;/p&gt;
&lt;h2 id=&#34;uefi-simple-適合用來做什麼&#34;&gt;&lt;code&gt;uefi-simple&lt;/code&gt; 適合用來做什麼
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;uefi-simple&lt;/code&gt; 適合做 UEFI 入門的第一塊踏板。&lt;/p&gt;
&lt;p&gt;它主要解決三個問題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;給你一個最小可編譯的 UEFI 應用結構&lt;/li&gt;
&lt;li&gt;幫你避開一開始就接觸大型韌體工程的複雜度&lt;/li&gt;
&lt;li&gt;讓你能先驗證編譯、連結、執行鏈路是否通&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這個專案支援不同建構方式，包括 Visual Studio 2022 和 MinGW/gcc，也可以配合 QEMU 與 OVMF 做測試。&lt;/p&gt;
&lt;p&gt;也就是說，你不一定非要準備真實機器反覆重開機測試。先用模擬器把程式跑起來，會安全很多。&lt;/p&gt;
&lt;h2 id=&#34;入門前要準備哪些東西&#34;&gt;入門前要準備哪些東西
&lt;/h2&gt;&lt;p&gt;最少需要準備幾類工具。&lt;/p&gt;
&lt;p&gt;第一類是編譯工具鏈。&lt;/p&gt;
&lt;p&gt;如果你在 Windows 上，可以優先考慮：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio 2022&lt;/li&gt;
&lt;li&gt;或 MinGW/gcc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第二類是 UEFI 執行環境。&lt;/p&gt;
&lt;p&gt;可以有兩種選擇：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用真實機器的 UEFI Shell 執行 &lt;code&gt;.EFI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用 QEMU + OVMF 在虛擬環境裡測試&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第三類是範例專案。&lt;/p&gt;
&lt;p&gt;新手不建議從空目錄開始手寫建構腳本。直接使用 &lt;code&gt;uefi-simple&lt;/code&gt; 這類最小範例，可以少踩很多建構系統的坑。&lt;/p&gt;
&lt;h2 id=&#34;大致流程&#34;&gt;大致流程
&lt;/h2&gt;&lt;p&gt;一個最小 UEFI 程式的入門流程可以這樣理解。&lt;/p&gt;
&lt;p&gt;第一步，取得範例專案。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;https&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;//&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;github&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pbatard&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;uefi-simple&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;第二步，選擇建構工具鏈。&lt;/p&gt;
&lt;p&gt;如果用 Visual Studio，就按專案裡的 Visual Studio 方案建構。&lt;br&gt;
如果用 MinGW/gcc，就按專案提供的 Makefile 或說明走。&lt;/p&gt;
&lt;p&gt;第三步，產生 &lt;code&gt;.EFI&lt;/code&gt; 檔案。&lt;/p&gt;
&lt;p&gt;這裡最關鍵的是確認目標架構。常見 PC 一般是 &lt;code&gt;x86_64&lt;/code&gt;，也就是 64 位元 UEFI 環境。&lt;/p&gt;
&lt;p&gt;第四步，把 &lt;code&gt;.EFI&lt;/code&gt; 放到可以被 UEFI Shell 存取的位置。&lt;/p&gt;
&lt;p&gt;如果用真實機器，通常會準備一個 FAT32 分割區或 USB 隨身碟。&lt;br&gt;
如果用 QEMU，可以把目錄或映像檔掛載進去。&lt;/p&gt;
&lt;p&gt;第五步，在 UEFI Shell 裡執行。&lt;/p&gt;
&lt;p&gt;執行效果通常就是一個最小輸出，比如印出類似 Hello World 的內容。&lt;/p&gt;
&lt;h2 id=&#34;最容易卡住的地方&#34;&gt;最容易卡住的地方
&lt;/h2&gt;&lt;p&gt;編譯 UEFI 程式最容易卡的不是 C 語言本身，而是環境和連結。&lt;/p&gt;
&lt;p&gt;常見問題包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;編譯器架構不對&lt;/li&gt;
&lt;li&gt;目標格式不對&lt;/li&gt;
&lt;li&gt;連結參數不完整&lt;/li&gt;
&lt;li&gt;缺少 UEFI 進入點&lt;/li&gt;
&lt;li&gt;產生的是普通可執行檔，不是 UEFI 能載入的 &lt;code&gt;.EFI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;QEMU 或 OVMF 沒配置好&lt;/li&gt;
&lt;li&gt;真實機器 Secure Boot 阻止執行未簽署程式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;尤其是連結器錯誤，經常會讓新手誤以為程式碼寫錯了。&lt;br&gt;
實際上，很多時候是進入函式、子系統、目標架構或連結腳本配置不對。&lt;/p&gt;
&lt;p&gt;所以第一階段不要急著改複雜邏輯。先確保原始範例能編譯、能執行，再一點點改輸出內容。&lt;/p&gt;
&lt;h2 id=&#34;測試時為什麼推薦-qemu--ovmf&#34;&gt;測試時為什麼推薦 QEMU + OVMF
&lt;/h2&gt;&lt;p&gt;真實機器測試 UEFI 程式並不是不行，但新手階段不太方便。&lt;/p&gt;
&lt;p&gt;因為你可能需要反覆：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;編譯&lt;/li&gt;
&lt;li&gt;複製到 USB 隨身碟&lt;/li&gt;
&lt;li&gt;重開機&lt;/li&gt;
&lt;li&gt;進入 UEFI Shell&lt;/li&gt;
&lt;li&gt;執行&lt;/li&gt;
&lt;li&gt;記錄錯誤&lt;/li&gt;
&lt;li&gt;回到系統修改&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這個循環很慢。&lt;/p&gt;
&lt;p&gt;QEMU + OVMF 的好處是可以在作業系統裡直接模擬 UEFI 環境。你可以更快地驗證 &lt;code&gt;.EFI&lt;/code&gt; 是否能被載入，也不容易影響真實機器啟動項。&lt;/p&gt;
&lt;p&gt;等程式基本跑通，再放到真實機器上測試，會更穩。&lt;/p&gt;
&lt;h2 id=&#34;新手應該先改哪裡&#34;&gt;新手應該先改哪裡
&lt;/h2&gt;&lt;p&gt;如果你已經用範例專案編譯出了第一個 &lt;code&gt;.EFI&lt;/code&gt;，下一步不要馬上寫複雜功能。&lt;/p&gt;
&lt;p&gt;建議按這個順序改：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先改輸出文字，確認重新編譯後的程式確實生效。&lt;/li&gt;
&lt;li&gt;再嘗試讀取 UEFI 提供的簡單資訊。&lt;/li&gt;
&lt;li&gt;再理解進入函式、輸出協定和基本服務。&lt;/li&gt;
&lt;li&gt;最後再考慮檔案系統、圖形輸出、啟動項管理等複雜功能。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這樣做的好處是每一步都能驗證。&lt;br&gt;
如果一上來就改很多東西，出錯時很難判斷到底是程式碼問題、編譯問題，還是執行環境問題。&lt;/p&gt;
&lt;h2 id=&#34;和普通-c-程式有什麼不同&#34;&gt;和普通 C 程式有什麼不同
&lt;/h2&gt;&lt;p&gt;UEFI 程式雖然可以用 C 寫，但它和普通 C 程式的執行環境完全不同。&lt;/p&gt;
&lt;p&gt;普通 C 程式通常執行在作業系統裡，可以依賴標準函式庫、檔案系統、程序模型和系統呼叫。&lt;/p&gt;
&lt;p&gt;UEFI 程式執行在作業系統啟動之前，它依賴的是 UEFI 韌體提供的服務。很多你在普通程式裡習慣使用的東西，在這裡並不是天然可用。&lt;/p&gt;
&lt;p&gt;所以寫 UEFI 程式時，要先適應幾個變化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;進入函式不一樣&lt;/li&gt;
&lt;li&gt;輸出方式不一樣&lt;/li&gt;
&lt;li&gt;可用函式庫不一樣&lt;/li&gt;
&lt;li&gt;記憶體和檔案存取方式不一樣&lt;/li&gt;
&lt;li&gt;除錯方式不一樣&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這也是為什麼推薦先從最小範例開始，而不是直接照普通 C 程式的習慣寫。&lt;/p&gt;
&lt;h2 id=&#34;一個比較現實的學習路線&#34;&gt;一個比較現實的學習路線
&lt;/h2&gt;&lt;p&gt;如果只是入門，可以按這個路線走：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一步：編譯 &lt;code&gt;uefi-simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第二步：用 QEMU + OVMF 跑起來&lt;/li&gt;
&lt;li&gt;第三步：修改 Hello World 輸出&lt;/li&gt;
&lt;li&gt;第四步：理解 UEFI Shell 怎麼載入 &lt;code&gt;.EFI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第五步：學習 UEFI 的進入函式和基本輸出協定&lt;/li&gt;
&lt;li&gt;第六步：再看 EDK II 或更完整的 UEFI 開發資料&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這條路線的重點是先讓回饋閉環跑起來。&lt;/p&gt;
&lt;p&gt;只要你能從原始碼產生 &lt;code&gt;.EFI&lt;/code&gt;，再在 UEFI 環境裡看到輸出，就已經跨過了最難的第一道門檻。&lt;/p&gt;
&lt;h2 id=&#34;參考&#34;&gt;參考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/pbatard/uefi-simple&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;pbatard/uefi-simple&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://zhuanlan.zhihu.com/p/643704056&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;知乎：編譯 UEFI 程式相關資料&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;最後一句&#34;&gt;最後一句
&lt;/h2&gt;&lt;p&gt;編譯第一個 UEFI 程式，難點通常不在「寫出一段 C 程式碼」，而在把工具鏈、連結格式和執行環境串起來。&lt;/p&gt;
&lt;p&gt;先別急著做複雜功能。&lt;br&gt;
從 &lt;code&gt;uefi-simple&lt;/code&gt; 這種最小範例開始，先得到一個能執行的 &lt;code&gt;.EFI&lt;/code&gt;，再逐步理解 UEFI 的進入點、協定和建構方式，會輕鬆很多。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
