Table of Contents#
- Understanding Linux Kernel Configuration and Dependencies
- Challenges of Manual Dependency Disabling
- Automating Recursive Disabling with Kconfiglib
- Testing and Validating the Modified Configuration
- Troubleshooting Common Issues
- Conclusion
- References
1. Understanding Linux Kernel Configuration and Dependencies#
What is Kconfig?#
Kconfig is the configuration system used by the Linux kernel to define build-time options. Every kernel subsystem (e.g., networking, storage) has a Kconfig file that declares options, their descriptions, and dependencies. The user selects options via tools like make menuconfig or make xconfig, and the selections are stored in a .config file at the kernel source root.
Types of Dependencies#
Kernel options depend on other options to ensure the build is valid. The most common dependency types are:
depends on: Weak dependency. IfCONFIG_A depends on CONFIG_B,CONFIG_Acan only be enabled ifCONFIG_Bis enabled. DisablingCONFIG_BforcesCONFIG_Ato be disabled.select: Strong dependency. IfCONFIG_A select CONFIG_B, enablingCONFIG_Aautomatically enablesCONFIG_B(even ifCONFIG_Bis disabled by the user).selectoverridesdepends onforCONFIG_B.imply: Suggestive dependency. IfCONFIG_A imply CONFIG_B, enablingCONFIG_Asuggests enablingCONFIG_B(but the user can still disableCONFIG_Bmanually).
Why Recursive Disabling Matters#
Suppose you want to disable CONFIG_DEBUG_FS (a debugging filesystem) to reduce kernel size. If CONFIG_DEBUG_FS is a dependency for CONFIG_TRACEPOINTS and CONFIG_KPROBES, those options will fail to build if CONFIG_DEBUG_FS is disabled but they remain enabled. To avoid build errors, you must first disable CONFIG_TRACEPOINTS and CONFIG_KPROBES—and any options that depend on them, recursively.
Manual tracking of these dependencies is impractical for large configurations. Automation is critical to avoid missing dependencies and ensure a valid build.
2. Challenges of Manual Dependency Disabling#
Even experienced developers struggle with manual dependency management:
- Hidden Dependencies: Dependencies may be nested (e.g.,
A → B → C), requiring multiple levels of checks. selectOverrides: IfCONFIG_X select Y, disablingYmanually will fail ifXis still enabled.- Configuration Drift: After disabling options, running
make olddefconfig(which updates the config with defaults) may re-enable dependencies, breaking your intended setup. - Time-Consuming: For complex subsystems (e.g.,
CONFIG_NET), disabling all dependencies could take hours of manual work.
3. Automating Recursive Disabling with Kconfiglib#
What is Kconfiglib?#
Kconfiglib is a Python library for parsing and manipulating Kconfig configurations. It provides programmatic access to kernel options, their dependencies, and relationships, making it ideal for automating config modifications. Unlike interactive tools like menuconfig, Kconfiglib lets you script dependency traversal and option toggling.
Installing Kconfiglib#
Kconfiglib requires Python 3.6+. Install it via pip:
pip3 install kconfiglibParsing Kernel Configurations#
To use Kconfiglib, you need:
- The kernel source tree (to access
Kconfigfiles). - A base
.configfile (e.g., frommake defconfigor a distribution-specific config likearch/x86/configs/defconfig).
Kconfiglib parses the entire Kconfig tree and the .config file to build a model of all options and their dependencies.
Recursive Dependency Disabler Script#
Below is a Python script that disables a target CONFIG option and all options that depend on it (recursively). It uses Kconfiglib to traverse reverse dependencies (rev_deps) and disable them in the correct order.
Step 1: Script Overview#
The script will:
- Load the kernel’s Kconfig tree and existing
.config. - Identify the target option (e.g.,
CONFIG_DEBUG_FS). - Recursively collect all options that depend on the target (reverse dependencies).
- Disable the collected dependencies and the target option.
- Save the modified configuration to a new file (e.g.,
.config.new).
Step 2: The Script#
import sys
from kconfiglib import Kconfig, Symbol
def collect_rev_deps(symbol, visited=None):
"""Recursively collect all symbols that depend on 'symbol' (reverse dependencies)."""
if visited is None:
visited = set()
if symbol in visited:
return visited
visited.add(symbol)
# Traverse all symbols that directly depend on 'symbol' (rev_deps)
for rev_dep in symbol.rev_deps:
# Skip non-user-configurable symbols (e.g., 'not set' or hardcoded)
if rev_dep.configurable:
collect_rev_deps(rev_dep, visited)
return visited
def disable_symbols(kconfig, symbols):
"""Disable a list of symbols in the Kconfig configuration."""
for sym in symbols:
if not sym.configurable:
print(f"Skipping non-configurable symbol: {sym.name}")
continue
# Disable the symbol (set to 'n')
sym.set_value('n')
print(f"Disabled: {sym.name}")
def main():
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} <kernel_source_dir> <target_config>")
print(f"Example: {sys.argv[0]} ~/linux-src CONFIG_DEBUG_FS")
sys.exit(1)
kernel_src = sys.argv[1]
target_config = sys.argv[2]
# Load the Kconfig tree and .config (uses .config in kernel_src by default)
kconfig = Kconfig(f"{kernel_src}/Kconfig", warn=False)
# Find the target symbol
target_sym = kconfig.get_symbol(target_config)
if not target_sym:
print(f"Error: Symbol {target_config} not found in Kconfig.")
sys.exit(1)
if not target_sym.configurable:
print(f"Error: {target_config} is not user-configurable.")
sys.exit(1)
# Collect all reverse dependencies (symbols that depend on the target)
print(f"Collecting reverse dependencies for {target_config}...")
rev_deps = collect_rev_deps(target_sym)
# Exclude the target itself from the initial list (disable it last)
rev_deps.discard(target_sym)
# Disable dependencies first, then the target
print(f"Disabling {len(rev_deps)} dependencies...")
disable_symbols(kconfig, rev_deps)
print(f"Disabling target: {target_config}")
disable_symbols(kconfig, [target_sym])
# Save the modified config
with open(f"{kernel_src}/.config.new", "w") as f:
f.write(kconfig.write_config())
print(f"Modified config saved to {kernel_src}/.config.new")
if __name__ == "__main__":
main()Step 3: Using the Script#
-
Prepare the Kernel Source: Ensure you have a kernel source tree with a valid
.config(e.g., runmake defconfigto generate a default config). -
Run the Script: Specify the kernel source directory and target CONFIG option:
python3 disable_deps.py ~/linux-src CONFIG_DEBUG_FS -
Output: The script will:
- Collect all symbols depending on
CONFIG_DEBUG_FS(e.g.,CONFIG_TRACEPOINTS). - Disable those symbols and
CONFIG_DEBUG_FS. - Save the result to
~/linux-src/.config.new.
- Collect all symbols depending on
4. Testing and Validating the Modified Configuration#
Sanity Checks with make olddefconfig#
After generating .config.new, validate it to ensure no dependencies are broken:
cd ~/linux-src
mv .config.new .config
make olddefconfig # Updates config with defaults for missing optionsmake olddefconfig will resolve any remaining dependencies and print warnings if critical options are disabled. If you see errors like error: "CONFIG_A" depends on "CONFIG_B" which is not set, the script may have missed a dependency (see Troubleshooting below).
Building and Testing the Kernel#
Build the kernel with the modified config and test it in a VM or on hardware:
make -j$(nproc) # Build the kernel
# Install modules and kernel (adjust for your distro)
sudo make modules_install installBoot the new kernel and verify the target option is disabled (e.g., check /proc/config.gz):
zcat /proc/config.gz | grep CONFIG_DEBUG_FS
# Should output: # CONFIG_DEBUG_FS is not set5. Troubleshooting Common Issues#
Circular Dependencies#
Problem: Some options depend on each other (e.g., A depends on B and B depends on A). The script may get stuck in a loop.
Fix: Kconfiglib’s rev_deps includes a visited set to avoid cycles, but manual intervention may be needed for complex cases. Use make menuconfig to search for circular dependencies.
Unavoidable Dependencies#
Problem: Core kernel options (e.g., CONFIG_64BIT, CONFIG_SMP) cannot be disabled, as they are required for basic functionality.
Fix: The script skips non-configurable symbols, but if you see Error: CONFIG_X is not user-configurable, choose a different target.
Handling select and imply#
Problem: The script ignores select dependencies. If CONFIG_A select B, disabling B will fail if A is still enabled.
Fix: Extend the script to check symbol.selected_by (symbols that select the target) and disable them first. For example:
def collect_selectors(symbol, visited=None):
"""Collect symbols that 'select' the target symbol."""
if visited is None:
visited = set()
if symbol in visited:
return visited
visited.add(symbol)
for selector in symbol.selected_by:
if selector.configurable:
collect_selectors(selector, visited)
return visitedAdd this to the script to disable selectors before dependencies.
6. Conclusion#
Recursively disabling kernel CONFIG dependencies manually is error-prone and inefficient. By using Kconfiglib to script dependency traversal, you can automate the process, ensuring all dependencies are disabled and your kernel config is valid. This approach saves time, reduces errors, and gives you fine-grained control over kernel features—critical for embedded systems, security-hardened kernels, or minimal builds.