<?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 Blog</title>
        <link>https://www.knightli.com/en/tags/c/</link>
        <description>Recent content in C on KnightLi Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Thu, 30 Apr 2026 19:53:08 +0800</lastBuildDate><atom:link href="https://www.knightli.com/en/tags/c/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Getting Started with Compiling UEFI Programs: From uefi-simple to Your First .EFI</title>
        <link>https://www.knightli.com/en/2026/04/30/compile-uefi-program-beginner-guide/</link>
        <pubDate>Thu, 30 Apr 2026 19:53:08 +0800</pubDate>
        
        <guid>https://www.knightli.com/en/2026/04/30/compile-uefi-program-beginner-guide/</guid>
        <description>&lt;p&gt;Compiling your first UEFI program is not exactly effortless. Environment setup can take time, linker errors are common, and a &lt;code&gt;.EFI&lt;/code&gt; program does not have the same direct edit-and-run experience as an ordinary desktop application.&lt;/p&gt;
&lt;p&gt;This article organizes the topic from a beginner&amp;rsquo;s perspective: if you only want to compile your first UEFI program, where should you start, which concepts matter first, and which pitfalls are most likely to appear?&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-uefi-program&#34;&gt;What Is a UEFI Program?
&lt;/h2&gt;&lt;p&gt;A UEFI program is usually a &lt;code&gt;.EFI&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;It is not an ordinary &lt;code&gt;.exe&lt;/code&gt; that you double-click in Windows. It is a PE/COFF executable that runs inside the UEFI firmware environment. Common use cases include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Boot managers&lt;/li&gt;
&lt;li&gt;Hardware initialization tools&lt;/li&gt;
&lt;li&gt;Firmware update tools&lt;/li&gt;
&lt;li&gt;Pre-boot diagnostic tools&lt;/li&gt;
&lt;li&gt;Custom boot flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many functions you see early in the system boot process may be related to UEFI applications, drivers, or firmware services.&lt;/p&gt;
&lt;p&gt;For beginners, there is no need to understand full firmware development immediately. The first goal is simple: compile a &lt;code&gt;.EFI&lt;/code&gt; file that can be loaded by a UEFI Shell or emulator.&lt;/p&gt;
&lt;h2 id=&#34;why-not-start-with-edk-ii&#34;&gt;Why Not Start with EDK II?
&lt;/h2&gt;&lt;p&gt;Real UEFI development often involves EDK II.&lt;/p&gt;
&lt;p&gt;EDK II is complete and closer to real firmware engineering, but it is not very friendly for beginners:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The project structure is complex&lt;/li&gt;
&lt;li&gt;The build system has a learning curve&lt;/li&gt;
&lt;li&gt;Environment variables and toolchain setup involve many details&lt;/li&gt;
&lt;li&gt;Compiler errors are not always easy to understand&lt;/li&gt;
&lt;li&gt;It is easy to get stuck on the environment before writing any code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the goal is simply to get a minimal UEFI program running, a lightweight example is a better starting point.&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; is one such project. Its goal is straightforward: provide a simple UEFI Hello World example so you can compile a &lt;code&gt;.EFI&lt;/code&gt; file first.&lt;/p&gt;
&lt;h2 id=&#34;what-is-uefi-simple-good-for&#34;&gt;What Is &lt;code&gt;uefi-simple&lt;/code&gt; Good For?
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;uefi-simple&lt;/code&gt; is a good first stepping stone for UEFI beginners.&lt;/p&gt;
&lt;p&gt;It solves three practical problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It gives you a minimal compilable UEFI application structure&lt;/li&gt;
&lt;li&gt;It avoids the complexity of large firmware projects at the beginning&lt;/li&gt;
&lt;li&gt;It lets you verify that compiling, linking, and running all work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The project supports multiple build methods, including Visual Studio 2022 and MinGW/gcc. It can also be tested with QEMU and OVMF.&lt;/p&gt;
&lt;p&gt;In other words, you do not have to repeatedly reboot a real machine for early experiments. Running the program in an emulator first is much safer.&lt;/p&gt;
&lt;h2 id=&#34;what-to-prepare-before-starting&#34;&gt;What to Prepare Before Starting
&lt;/h2&gt;&lt;p&gt;You need at least a few categories of tools.&lt;/p&gt;
&lt;p&gt;The first category is the compiler toolchain.&lt;/p&gt;
&lt;p&gt;On Windows, you can start with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio 2022&lt;/li&gt;
&lt;li&gt;Or MinGW/gcc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The second category is a UEFI runtime environment.&lt;/p&gt;
&lt;p&gt;There are two common options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run the &lt;code&gt;.EFI&lt;/code&gt; file in a real machine&amp;rsquo;s UEFI Shell&lt;/li&gt;
&lt;li&gt;Test it in a virtual environment with QEMU + OVMF&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The third category is an example project.&lt;/p&gt;
&lt;p&gt;Beginners should not start by writing build scripts from an empty directory. Using a minimal example such as &lt;code&gt;uefi-simple&lt;/code&gt; helps avoid many build-system problems.&lt;/p&gt;
&lt;h2 id=&#34;basic-workflow&#34;&gt;Basic Workflow
&lt;/h2&gt;&lt;p&gt;A minimal UEFI program workflow can be understood like this.&lt;/p&gt;
&lt;p&gt;First, get the example project.&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;Second, choose a build toolchain.&lt;/p&gt;
&lt;p&gt;If you use Visual Studio, build with the Visual Studio solution in the project.&lt;br&gt;
If you use MinGW/gcc, follow the Makefile or instructions provided by the project.&lt;/p&gt;
&lt;p&gt;Third, generate the &lt;code&gt;.EFI&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The key point here is to confirm the target architecture. A common PC is usually &lt;code&gt;x86_64&lt;/code&gt;, meaning a 64-bit UEFI environment.&lt;/p&gt;
&lt;p&gt;Fourth, put the &lt;code&gt;.EFI&lt;/code&gt; file somewhere the UEFI Shell can access.&lt;/p&gt;
&lt;p&gt;On a real machine, this usually means preparing a FAT32 partition or USB drive.&lt;br&gt;
With QEMU, you can mount a directory or disk image.&lt;/p&gt;
&lt;p&gt;Fifth, run it in the UEFI Shell.&lt;/p&gt;
&lt;p&gt;The result is usually a minimal output, such as a Hello World-style message.&lt;/p&gt;
&lt;h2 id=&#34;where-beginners-usually-get-stuck&#34;&gt;Where Beginners Usually Get Stuck
&lt;/h2&gt;&lt;p&gt;The hardest part of compiling a UEFI program is usually not the C language itself, but the environment and linking process.&lt;/p&gt;
&lt;p&gt;Common issues include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wrong compiler architecture&lt;/li&gt;
&lt;li&gt;Wrong target format&lt;/li&gt;
&lt;li&gt;Incomplete linker parameters&lt;/li&gt;
&lt;li&gt;Missing UEFI entry point&lt;/li&gt;
&lt;li&gt;Generating an ordinary executable instead of a UEFI-loadable &lt;code&gt;.EFI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;QEMU or OVMF not configured correctly&lt;/li&gt;
&lt;li&gt;Secure Boot on a real machine blocking an unsigned program&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Linker errors are especially easy to misread as code problems.&lt;br&gt;
In many cases, the real issue is the entry function, subsystem, target architecture, or linker script.&lt;/p&gt;
&lt;p&gt;So in the first stage, do not rush into complex logic. Make sure the original example can compile and run, then change the output little by little.&lt;/p&gt;
&lt;h2 id=&#34;why-use-qemu--ovmf-for-testing&#34;&gt;Why Use QEMU + OVMF for Testing?
&lt;/h2&gt;&lt;p&gt;Testing UEFI programs on a real machine is possible, but it is not convenient at the beginner stage.&lt;/p&gt;
&lt;p&gt;You may have to repeat this cycle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compile&lt;/li&gt;
&lt;li&gt;Copy to a USB drive&lt;/li&gt;
&lt;li&gt;Reboot&lt;/li&gt;
&lt;li&gt;Enter the UEFI Shell&lt;/li&gt;
&lt;li&gt;Run the program&lt;/li&gt;
&lt;li&gt;Record the error&lt;/li&gt;
&lt;li&gt;Return to the system and modify the code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That loop is slow.&lt;/p&gt;
&lt;p&gt;QEMU + OVMF lets you simulate a UEFI environment directly inside the operating system. You can verify whether a &lt;code&gt;.EFI&lt;/code&gt; file loads more quickly, and it is less likely to affect your real boot entries.&lt;/p&gt;
&lt;p&gt;Once the program basically works, testing it on a real machine is much more manageable.&lt;/p&gt;
&lt;h2 id=&#34;what-should-beginners-modify-first&#34;&gt;What Should Beginners Modify First?
&lt;/h2&gt;&lt;p&gt;If you have already compiled your first &lt;code&gt;.EFI&lt;/code&gt; with the example project, do not jump into complex features immediately.&lt;/p&gt;
&lt;p&gt;A better order is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change the output text first to confirm that recompilation really takes effect.&lt;/li&gt;
&lt;li&gt;Try reading simple information provided by UEFI.&lt;/li&gt;
&lt;li&gt;Understand the entry function, output protocol, and basic services.&lt;/li&gt;
&lt;li&gt;Then consider more complex features such as file systems, graphical output, or boot entry management.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach makes every step verifiable.&lt;br&gt;
If you change too much at once, it becomes difficult to tell whether the issue is in the code, the build process, or the runtime environment.&lt;/p&gt;
&lt;h2 id=&#34;how-is-it-different-from-an-ordinary-c-program&#34;&gt;How Is It Different from an Ordinary C Program?
&lt;/h2&gt;&lt;p&gt;Although UEFI programs can be written in C, their runtime environment is completely different from ordinary C programs.&lt;/p&gt;
&lt;p&gt;An ordinary C program usually runs inside an operating system and can rely on the standard library, file system, process model, and system calls.&lt;/p&gt;
&lt;p&gt;A UEFI program runs before the operating system boots. It relies on services provided by UEFI firmware. Many things you are used to in normal programs are not automatically available here.&lt;/p&gt;
&lt;p&gt;When writing UEFI programs, you need to adapt to several differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The entry function is different&lt;/li&gt;
&lt;li&gt;Output works differently&lt;/li&gt;
&lt;li&gt;Available libraries are different&lt;/li&gt;
&lt;li&gt;Memory and file access work differently&lt;/li&gt;
&lt;li&gt;Debugging works differently&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is why starting from a minimal example is better than writing code as if it were a normal C program.&lt;/p&gt;
&lt;h2 id=&#34;a-practical-learning-path&#34;&gt;A Practical Learning Path
&lt;/h2&gt;&lt;p&gt;For beginners, a realistic path looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1: Compile &lt;code&gt;uefi-simple&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 2: Run it with QEMU + OVMF&lt;/li&gt;
&lt;li&gt;Step 3: Modify the Hello World output&lt;/li&gt;
&lt;li&gt;Step 4: Understand how the UEFI Shell loads &lt;code&gt;.EFI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 5: Learn the UEFI entry function and basic output protocol&lt;/li&gt;
&lt;li&gt;Step 6: Then read EDK II or more complete UEFI development material&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The point of this path is to build a working feedback loop first.&lt;/p&gt;
&lt;p&gt;Once you can generate a &lt;code&gt;.EFI&lt;/code&gt; from source and see output in a UEFI environment, you have already crossed the hardest first threshold.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;Zhihu: UEFI program compilation material&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;final-thought&#34;&gt;Final Thought
&lt;/h2&gt;&lt;p&gt;The hard part of compiling your first UEFI program is usually not writing a bit of C code, but connecting the toolchain, link format, and runtime environment.&lt;/p&gt;
&lt;p&gt;Do not rush into complex features.&lt;br&gt;
Start with a minimal example such as &lt;code&gt;uefi-simple&lt;/code&gt;, get a runnable &lt;code&gt;.EFI&lt;/code&gt; first, and then gradually understand UEFI entry points, protocols, and build methods.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
