<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[How does the Linux kernel build system (Kbuild) detect file changes?]]></title><description><![CDATA[<p dir="auto">The Linux kernel build system (Kbuild) detects changed files using a combination of make's timestamp comparison and a sophisticated Dependency Tracking mechanism.</p>
<p dir="auto">As you observed, changing a single .c file finishes quickly, whereas modifying a header (.h) file often triggers a massive rebuild. This happens because of the structure of the Dependency Graph.<br />
Here is the technical breakdown of how this works:</p>
<ol>
<li>Core Detection Mechanism: Hidden .cmd Files<br />
When the kernel is compiled, Kbuild doesn't just generate the .o (object) file; it also creates a hidden file that records a list of every single file referenced during the creation of that object.</li>
</ol>
<ul>
<li>Example: Let's say you are compiling drivers/net/ethernet/intel/e1000/e1000_main.c.</li>
<li>The build system generates e1000_main.o and simultaneously creates a hidden file named .e1000_main.o.cmd in the same directory.<br />
If you open this .cmd file, you will see something like this (it is very long):</li>
</ul>
<pre><code>cmd_drivers/net/ethernet/intel/e1000/e1000_main.o := gcc ... (compiler options) ... \
  source_drivers/net/ethernet/intel/e1000/e1000_main.c \
  deps_drivers/net/ethernet/intel/e1000/e1000_main.o := \
    drivers/net/ethernet/intel/e1000/e1000_main.c \
    include/linux/module.h \
    include/linux/types.h \
    include/linux/init.h \
    ... (hundreds of other header files) ...
</code></pre>
<p dir="auto">The next time you run make, the process is as follows:</p>
<ul>
<li>It checks if e1000_main.o exists.</li>
<li>It reads the .e1000_main.o.cmd file.</li>
<li>If the modification time (timestamp) of any source or header file listed there is newer than the e1000_main.o file, make determines that the object is "outdated" and recompiles it.</li>
</ul>
<ol start="2">
<li>Why Does Modifying a Header Trigger a Full Rebuild?<br />
This is due to the difference in the Scope of Reference.<br />
A. When modifying a .c file (Leaf Node Change)</li>
</ol>
<ul>
<li>my_driver.c is typically not included by other files. It sits at the edge (leaf) of the dependency tree.</li>
<li>Therefore, if my_driver.c changes, only my_driver.o needs to be rebuilt.<br />
B. When modifying a .h file (Root/Branch Node Change)</li>
<li>Take a core header like include/linux/sched.h (related to process scheduling) as an example.</li>
<li>This header is #included by almost every subsystem in the kernel (filesystems, memory management, drivers, etc.).</li>
<li>Consequently, include/linux/sched.h appears in the dependency lists within thousands of .cmd files.</li>
<li>The moment this header is touched, make sees that "thousands of .o files that depend on this header are outdated," and triggers a rebuild for all of them.</li>
</ul>
<blockquote>
<p dir="auto">Note: In C, recompilation upon header modification is strictly necessary. If structure (struct) member offsets change or macro (#define) values are updated, the binary code utilizing them must be regenerated to calculate the new offsets/values correctly.</p>
</blockquote>
<ol start="3">
<li>Technical Implementation Tool: fixdep<br />
To optimize this process, the Linux kernel build system uses a tool called fixdep.</li>
</ol>
<ul>
<li>When compiling with GCC, the -Wp,-MMD,file.d option is used. This tells the compiler to output a list of all header files read during the Preprocessing stage.</li>
<li>The kernel's scripts/basic/fixdep program parses this list and converts it into the .cmd file format (Make rules) shown above.</li>
<li>It also tracks changes to command-line options (CFLAGS). If the compiler flags change, fixdep ensures a rebuild occurs even if the source files haven't changed.<br />
Summary</li>
<li>How does it know?: Every .o file has a hidden .cmd file that lists "every file used to build me (including headers)."</li>
<li>Why do headers take so long?: Because that single header file is listed in the .cmd dependency lists of thousands of object files.</li>
<li>Why are .c files fast?: They are rarely dependencies for other files, so only the file itself needs recompilation.<br />
Tip: How to build specific modules quickly?<br />
Running make from the kernel root checks the entire dependency tree, which can be slow. You can build specific directories to save time:</li>
</ul>
<pre><code># Build only the driver in this specific directory
make M=drivers/net/ethernet/intel/e1000/
</code></pre>
<p dir="auto">However, if you have modified a core header (include/linux/*.h), a full rebuild is unavoidable. Skipping it would likely result in a Symbol Version (CRC) mismatch, preventing the module from loading.</p>
<hr />
<h3>1. File Change Check After Build</h3>
<ol>
<li><strong>Checking Config (<code>m</code> vs <code>*</code> )</strong>: If it is a module (<code>m</code>), you only need to run <code>make modules_install</code> and reload it. If it is built-in (<code>*</code> or <code>y</code>), you must reinstall the kernel image (<code>bzImage</code>) and <strong>reboot</strong>.</li>
<li><strong>Checking Timestamps:</strong> <strong>Needs refinement.</strong> The <code>make</code> utility handles timestamp comparisons automatically.</li>
<li><strong>Comparing md5sum</strong>: The <code>.o</code> or <code>.ko</code> files in your build directory will have different hash values every time you build them (due to build paths, timestamps, etc.). Furthermore, installed modules undergo <strong>stripping</strong> (removal of debug symbols), making their hash different from the original build artifact.</li>
</ol>
<hr />
<h3>2. Verifying Steps</h3>
<h4>Step 1: Verify Config Status (Improve Precision)</h4>
<p dir="auto">Using <code>grep</code> is more reliable than visually checking menuconfig.</p>
<ul>
<li><strong>Method:</strong> Check the <code>.config</code> file in your working directory.</li>
</ul>
<pre><code class="language-bash">grep "CONFIG_MY_DRIVER" .config

</code></pre>
<ul>
<li><code>CONFIG_...=m</code>: It is a module. Just replace the <code>.ko</code> file (No reboot needed).</li>
<li><code>CONFIG_...=y</code>: It is built-in. You must flash the new kernel image and <strong>reboot</strong>.</li>
</ul>
<h4>Step 2: Verify Changes (Key Correction)</h4>
<p dir="auto">Comparing timestamps or md5sum hashes is inaccurate. Instead, verifying <strong>strings inside the binary</strong> or using <strong>Modinfo</strong> is the most definitive method.</p>
<p dir="auto"><strong>Recommended Method A: Use <code>strings</code> (Most Reliable)</strong><br />
Add a unique, identifiable string (like a log message) to your code, then check if that string exists in the binary.</p>
<ol>
<li>Add code: <code>printk(KERN_INFO "MyPatch_v1 checking...\n");</code></li>
<li>Build, then verify:</li>
</ol>
<pre><code class="language-bash">strings drivers/my/driver.ko | grep "MyPatch_v1"
</code></pre>
<ul>
<li>If you see the string, your changes are 100% applied.</li>
</ul>
<p dir="auto">**Recommended Method B: Check <code>modinfo**</code><br />
If you updated the module version, check it with <code>modinfo</code>.</p>
<pre><code class="language-bash">modinfo drivers/my/driver.ko
</code></pre>
<h4>Step 3: Note on Comparing with Installed Modules (Replacing hash sum command)</h4>
<p dir="auto">If you try to compare the "installed module (<code>/lib/modules/.../my.ko</code>)" with the "just built module (<code>my.ko</code>)" using like <code>md5sum</code>, it will fail 99% of the time.</p>
<ul>
<li><strong>Reason:</strong> During <code>make modules_install</code>, debug symbols are <strong>stripped</strong>, and module signatures are added.</li>
<li><strong>Alternative:</strong> Compare file sizes (the installed one will be much smaller) or use the <code>strings</code> command mentioned above to peek inside the installed module.</li>
</ul>
<hr />
<h3>3. Summary</h3>
<blockquote>
<p dir="auto">After modifying the kernel code, check the <code>.config</code> file to determine if the feature is a module (<code>m</code>) or built-in (<code>*</code>, <code>y</code>).</p>
<ol>
<li><strong>If Module (<code>m</code>)</strong>: Recompile only the module and reinstall it (<code>rmmod</code> -&gt; <code>insmod</code>).</li>
<li><strong>If Built-in (<code>y</code>)</strong>: You must reinstall the entire kernel image and reboot the system.</li>
</ol>
<p dir="auto"><strong>How to Verify Changes:</strong><br />
Simple timestamp or hash (md5sum) comparisons can be inaccurate due to symbol stripping or signing during installation. Instead, I recommend using the <strong><code>strings [module].ko | grep [unique_string]</code></strong> command to verify that your specific code changes are actually present in the binary.</p>
</blockquote>
]]></description><link>https://kernelmeet.com/topic/7/how-does-the-linux-kernel-build-system-kbuild-detect-file-changes</link><generator>RSS for Node</generator><lastBuildDate>Wed, 12 Aug 2026 18:05:14 GMT</lastBuildDate><atom:link href="https://kernelmeet.com/topic/7.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 30 Dec 2025 00:19:13 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to How does the Linux kernel build system (Kbuild) detect file changes? on Tue, 30 Dec 2025 14:23:10 GMT]]></title><description><![CDATA[<p dir="auto">Very good concise information! Keep it up.</p>
]]></description><link>https://kernelmeet.com/post/17</link><guid isPermaLink="true">https://kernelmeet.com/post/17</guid><dc:creator><![CDATA[jethawapravin]]></dc:creator><pubDate>Tue, 30 Dec 2025 14:23:10 GMT</pubDate></item></channel></rss>