<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>UEFI on KnightLi的博客</title>
        <link>https://www.knightli.com/tags/uefi/</link>
        <description>Recent content in UEFI on KnightLi的博客</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Thu, 30 Apr 2026 19:53:08 +0800</lastBuildDate><atom:link href="https://www.knightli.com/tags/uefi/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>编译 UEFI 程序入门：从 uefi-simple 到第一个 .EFI</title>
        <link>https://www.knightli.com/2026/04/30/compile-uefi-program-beginner-guide/</link>
        <pubDate>Thu, 30 Apr 2026 19:53:08 +0800</pubDate>
        
        <guid>https://www.knightli.com/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 分区或 U 盘。&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;拷贝到 U 盘&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>
