Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (Brite)
  • No Skin
Collapse

Linux Kernel Meet

  1. Home
  2. Blogs
  3. How does the Linux kernel build system (Kbuild) detect file changes?

How does the Linux kernel build system (Kbuild) detect file changes?

Scheduled Pinned Locked Moved Blogs
2 Posts 2 Posters 298 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Y Offline
    Y Offline
    Yunseong Kim
    wrote on last edited by Yunseong Kim
    #1

    The Linux kernel build system (Kbuild) detects changed files using a combination of make's timestamp comparison and a sophisticated Dependency Tracking mechanism.

    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.
    Here is the technical breakdown of how this works:

    1. Core Detection Mechanism: Hidden .cmd Files
      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.
    • Example: Let's say you are compiling drivers/net/ethernet/intel/e1000/e1000_main.c.
    • The build system generates e1000_main.o and simultaneously creates a hidden file named .e1000_main.o.cmd in the same directory.
      If you open this .cmd file, you will see something like this (it is very long):
    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) ...
    

    The next time you run make, the process is as follows:

    • It checks if e1000_main.o exists.
    • It reads the .e1000_main.o.cmd file.
    • 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.
    1. Why Does Modifying a Header Trigger a Full Rebuild?
      This is due to the difference in the Scope of Reference.
      A. When modifying a .c file (Leaf Node Change)
    • my_driver.c is typically not included by other files. It sits at the edge (leaf) of the dependency tree.
    • Therefore, if my_driver.c changes, only my_driver.o needs to be rebuilt.
      B. When modifying a .h file (Root/Branch Node Change)
    • Take a core header like include/linux/sched.h (related to process scheduling) as an example.
    • This header is #included by almost every subsystem in the kernel (filesystems, memory management, drivers, etc.).
    • Consequently, include/linux/sched.h appears in the dependency lists within thousands of .cmd files.
    • 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.

    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.

    1. Technical Implementation Tool: fixdep
      To optimize this process, the Linux kernel build system uses a tool called fixdep.
    • 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.
    • The kernel's scripts/basic/fixdep program parses this list and converts it into the .cmd file format (Make rules) shown above.
    • 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.
      Summary
    • How does it know?: Every .o file has a hidden .cmd file that lists "every file used to build me (including headers)."
    • Why do headers take so long?: Because that single header file is listed in the .cmd dependency lists of thousands of object files.
    • Why are .c files fast?: They are rarely dependencies for other files, so only the file itself needs recompilation.
      Tip: How to build specific modules quickly?
      Running make from the kernel root checks the entire dependency tree, which can be slow. You can build specific directories to save time:
    # Build only the driver in this specific directory
    make M=drivers/net/ethernet/intel/e1000/
    

    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.


    1. File Change Check After Build

    1. Checking Config (m vs * ): If it is a module (m), you only need to run make modules_install and reload it. If it is built-in (* or y), you must reinstall the kernel image (bzImage) and reboot.
    2. Checking Timestamps: Needs refinement. The make utility handles timestamp comparisons automatically.
    3. Comparing md5sum: The .o or .ko 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 stripping (removal of debug symbols), making their hash different from the original build artifact.

    2. Verifying Steps

    Step 1: Verify Config Status (Improve Precision)

    Using grep is more reliable than visually checking menuconfig.

    • Method: Check the .config file in your working directory.
    grep "CONFIG_MY_DRIVER" .config
    
    
    • CONFIG_...=m: It is a module. Just replace the .ko file (No reboot needed).
    • CONFIG_...=y: It is built-in. You must flash the new kernel image and reboot.

    Step 2: Verify Changes (Key Correction)

    Comparing timestamps or md5sum hashes is inaccurate. Instead, verifying strings inside the binary or using Modinfo is the most definitive method.

    Recommended Method A: Use strings (Most Reliable)
    Add a unique, identifiable string (like a log message) to your code, then check if that string exists in the binary.

    1. Add code: printk(KERN_INFO "MyPatch_v1 checking...\n");
    2. Build, then verify:
    strings drivers/my/driver.ko | grep "MyPatch_v1"
    
    • If you see the string, your changes are 100% applied.

    **Recommended Method B: Check modinfo**
    If you updated the module version, check it with modinfo.

    modinfo drivers/my/driver.ko
    

    Step 3: Note on Comparing with Installed Modules (Replacing hash sum command)

    If you try to compare the "installed module (/lib/modules/.../my.ko)" with the "just built module (my.ko)" using like md5sum, it will fail 99% of the time.

    • Reason: During make modules_install, debug symbols are stripped, and module signatures are added.
    • Alternative: Compare file sizes (the installed one will be much smaller) or use the strings command mentioned above to peek inside the installed module.

    3. Summary

    After modifying the kernel code, check the .config file to determine if the feature is a module (m) or built-in (*, y).

    1. If Module (m): Recompile only the module and reinstall it (rmmod -> insmod).
    2. If Built-in (y): You must reinstall the entire kernel image and reboot the system.

    How to Verify Changes:
    Simple timestamp or hash (md5sum) comparisons can be inaccurate due to symbol stripping or signing during installation. Instead, I recommend using the strings [module].ko | grep [unique_string] command to verify that your specific code changes are actually present in the binary.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jethawapravin
      wrote on last edited by
      #2

      Very good concise information! Keep it up.

      1 Reply Last reply
      1
      Reply
      • Reply as topic
      Log in to reply
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes


      • Login

      • Don't have an account? Register

      • Login or register to search.
      Powered by NodeBB Contributors
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • World
      • Users
      • Groups