<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Saeed</title><link>https://saeedvft.github.io/posts/</link><description>Recent content in Posts on Saeed</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>© 2026 Saeed</copyright><lastBuildDate>Wed, 14 Oct 2026 09:58:08 +0330</lastBuildDate><atom:link href="https://saeedvft.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>The 64-Byte Difference Between uImage and zImage</title><link>https://saeedvft.github.io/posts/14-10-2026-uimage-vs-zimage/</link><pubDate>Wed, 14 Oct 2026 09:58:08 +0330</pubDate><guid>https://saeedvft.github.io/posts/14-10-2026-uimage-vs-zimage/</guid><description>If you&amp;rsquo;ve spent any time in embedded Linux, you&amp;rsquo;ve run into zImage and uImage. You&amp;rsquo;ve probably also seen u-boot.bin sitting right next to them in the same directory, which leads to a very common question:
Are these kernel images or U-Boot images?
Short answer: They&amp;rsquo;re both kernel images. The &amp;ldquo;u&amp;rdquo; in uImage is misleading — it refers to the format, not the contents. Let&amp;rsquo;s break it down properly.
The Two Files at a Glance File What it actually is zImage A compressed, self-extracting Linux kernel uImage That same kernel, plus a 64-byte U-Boot header u-boot.</description><content type="html"><![CDATA[<p>If you&rsquo;ve spent any time in embedded Linux, you&rsquo;ve run into <code>zImage</code> and <code>uImage</code>. You&rsquo;ve probably also seen <code>u-boot.bin</code> sitting right next to them in the same directory, which leads to a very common question:</p>
<p><strong>Are these kernel images or U-Boot images?</strong></p>
<p>Short answer: <strong>They&rsquo;re both kernel images.</strong> The &ldquo;u&rdquo; in uImage is misleading — it refers to the format, not the contents. Let&rsquo;s break it down properly.</p>
<h2 id="the-two-files-at-a-glance">The Two Files at a Glance</h2>
<table>
<thead>
<tr>
<th>File</th>
<th>What it actually is</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>zImage</code></td>
<td>A compressed, self-extracting <strong>Linux kernel</strong></td>
</tr>
<tr>
<td><code>uImage</code></td>
<td>That same kernel, plus a <strong>64-byte U-Boot header</strong></td>
</tr>
<tr>
<td><code>u-boot.bin</code></td>
<td>The <strong>bootloader itself</strong> (a completely different thing)</td>
</tr>
</tbody>
</table>
<p>So when you see <code>uImage</code> in your boot partition, that&rsquo;s the kernel U-Boot will load — not U-Boot.</p>
<h2 id="what-is-a-zimage">What Is a zImage?</h2>
<p><code>zImage</code> is the compressed Linux kernel produced by the kernel build system. The &ldquo;z&rdquo; means compressed, and it&rsquo;s a <strong>self-extracting</strong> image: the first few bytes contain a small decompression stub. When the CPU jumps to the start of a zImage, that stub runs, decompresses the real kernel into memory, and hands off control.</p>
<p>You get it by building the kernel:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>make ARCH<span style="color:#f92672">=</span>arm zImage
</span></span></code></pre></div><p>No extra tooling required. The kernel build system produces it directly.</p>
<h2 id="what-is-a-uimage">What Is a uImage?</h2>
<p>A <code>uImage</code> is a zImage (or a raw <code>Image</code>) wrapped in a <strong>U-Boot-specific container format</strong>. It&rsquo;s created by a tool called <code>mkimage</code>, which lives in the U-Boot source tree:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkimage -A arm -O linux -T kernel -C none <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>        -a 0x8000 -e 0x8000 <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>        -n <span style="color:#e6db74">&#34;Linux Kernel&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>        -d zImage uImage
</span></span></code></pre></div><p>That command prepends a <strong>64-byte header</strong> containing metadata:</p>
<ul>
<li>OS type (Linux)</li>
<li>CPU architecture (ARM, MIPS, etc.)</li>
<li>Compression type</li>
<li>Load address</li>
<li>Entry point</li>
<li>A name string and a CRC checksum</li>
</ul>
<p>The header exists purely so the bootloader can parse the image before jumping into it.</p>
<h2 id="the-key-insight-strip-the-header-get-a-zimage">The Key Insight: Strip the Header, Get a zImage</h2>
<p>If you start executing a uImage at <strong>offset 64</strong> — skipping the header — it behaves exactly like a zImage. That&rsquo;s because it <em>is</em> a zImage underneath. The header adds no code, no compression, no functionality. It&rsquo;s just metadata for the bootloader.</p>
<p>This is why the file sizes differ by exactly 64 bytes.</p>
<h2 id="why-do-both-exist">Why Do Both Exist?</h2>
<p>The answer is bootloader history.</p>
<h3 id="legacy-u-boot--uimage">Legacy U-Boot → uImage</h3>
<p>Older versions of U-Boot only supported the <code>bootm</code> command, which <strong>requires</strong> a valid uImage header. It needs to know where to load the kernel, what architecture it&rsquo;s for, and where to jump. Without the header, <code>bootm</code> can&rsquo;t do its job. If you&rsquo;re stuck on an old bootloader, you must use a uImage.</p>
<h3 id="modern-u-boot--zimage">Modern U-Boot → zImage</h3>
<p>Newer U-Boot versions added the <code>bootz</code> command, which boots a zImage <strong>directly</strong> — no header needed. The bootloader figures out the rest. This is now the preferred path because:</p>
<ul>
<li>It removes the <code>mkimage</code> step from the build.</li>
<li>It&rsquo;s simpler and less error-prone.</li>
<li>It&rsquo;s the <strong>only option for ARM64</strong> — the kernel build system no longer supports generating uImages for 64-bit ARM at all.</li>
</ul>
<h2 id="the-naming-confusion-explained">The Naming Confusion, Explained</h2>
<p>Here&rsquo;s why people get tripped up:</p>
<ul>
<li><code>mkimage</code> is part of the <strong>U-Boot source tree</strong>.</li>
<li>The uImage format is <strong>defined by U-Boot</strong>.</li>
<li>So the header is &ldquo;U-Boot&rsquo;s header,&rdquo; applied to a kernel.</li>
</ul>
<p>That&rsquo;s it. The &ldquo;u&rdquo; describes the <strong>container format</strong>, not the <strong>payload</strong>. The payload is always the Linux kernel.</p>
<p>A quick sanity check on any real uImage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>file uImage
</span></span><span style="display:flex;"><span><span style="color:#75715e"># uImage, Linux kernel, ...</span>
</span></span></code></pre></div><p>The <code>file</code> utility confirms it: kernel payload, U-Boot container.</p>
<h2 id="which-one-should-you-use">Which One Should You Use?</h2>
<table>
<thead>
<tr>
<th>Situation</th>
<th>Use</th>
</tr>
</thead>
<tbody>
<tr>
<td>Legacy U-Boot (only <code>bootm</code>)</td>
<td><strong>uImage</strong></td>
</tr>
<tr>
<td>Modern U-Boot (<code>bootz</code> available)</td>
<td><strong>zImage</strong></td>
</tr>
<tr>
<td>ARM64 / AArch64</td>
<td><strong>zImage</strong> (uImage not supported)</td>
</tr>
<tr>
<td>You want fewer build steps</td>
<td><strong>zImage</strong></td>
</tr>
</tbody>
</table>
<p><strong>So a Rule of thumb:</strong> If your bootloader supports <code>bootz</code>, use zImage and skip <code>mkimage</code> entirely. If it doesn&rsquo;t, you&rsquo;re on old U-Boot and you need a uImage.</p>
]]></content></item></channel></rss>