Human Readable   

 

     
   
     

Compiling the 2.4.x Linux Kernel

© Copyright Darrell Anderson.

Download Download an Adobe PDF copy of this how-to.

Introduction

I do not consider myself a kernel guru by any measure of the word. I do not pretend to understand or know all the nuances of software compiling. I therefore wrote a guideline for myself to remember how to recompile the Linux kernel.

Many people have written how they compile a kernel, but one aspect often repeated by these authors is the presumption that other people intuitively know what to do between steps or that other people have configured their system exactly the same. Many of those authors provide no instructions for recovery when things go awry. As many novice compilers discover, often things do go awry and they do need a means of restoring their systems to working condition. Many first-timers initially experience trouble compiling a kernel, including me. Through my experiences and frustrations this guideline evolved into a detailed how-to.

As implied by the title of this document, I am focusing only on the 2.4.x series. As of the date of this guideline, I have not ventured into the world of the 2.6 kernel, although that version has been available for a few years. The simple reason is I am satisfied with the long-proven 2.4 kernel for my older hardware. One day I likely will venture into testing and installing the 2.6 kernel. Until then, this guideline remains useful for me and many other people.

I am writing this document from a Slackware perspective and with respect to older hardware such as I use. Therefore, if using a different GNU/Linux distro or newer hardware, know that you might need to modify this document according to your own needs and desires.

My initial motivation for wanting to recompile the stock Slackware kernel was not performance but several nuisance messages during the booting process. These messages were not fatal, but distracting. One infamous boot error message is the message of:

kmod: failed to exec /sbin/modprobe -s -k scsi_hostadapter, errno = 2

To my knowledge, eliminating this message is possible only by 1) recompiling the stock Slackware kernel or 2) booting with a ram disk. This error message occurs because in the stock Slackware, SCSI emulation support is compiled as a loadable module rather than built in. When the kernel boots and performs the basic drive scanning, the SCSI emulation module is not yet loaded. Hence the error message. After the module is loaded the SCSI emulation works as expected.

I also wanted to disable RAID and Multiple Disk support and other unnecessary components. I run old boxes and those kernel screen and log messages distract me when I have no need for such support. With recompiling I expect no significant performance improvements, but I do expect less overhead and a wee faster boot time because the kernel is not supporting unnecessary components. And there is the experience of better understanding how the Linux kernel functions.

In this guideline I will be recompiling the 2.4.31 kernel. For different versions merely modify the instructions accordingly.

Although there are X graphical tools for compiling a kernel, these instructions are written to run from the console outside of X. Compiling a new kernel takes time and especially with old hardware, running without X reduces compile times. If you have modern (fast) hardware then using those X-based tools should work fine for you.

Prerequisites and Precautions

Successfully compiling a kernel depends upon preparation. Here is some information that has helped me.

  1. The kernel sources are stored in:

    /usr/src/linux-2.4.31

  2. Usually there is a soft link to the current source directory named /usr/src/linux.
  3. The associated loadable kernel modules will be located in a directory of a similar name:

    /lib/modules/2.4.31.

  4. In /lib/modules/2.4.31 is a soft link called build. To my knowledge this soft link serves no run-time purpose and is informational only. The link is created when running the make modules_install command. The link points to the location where the loadable modules were compiled. In a stock GNU/Linux installation, that link points to /usr/src/linux or /usr/src/linux-2.4.31.
  5. In a stock installation the configuration file that is used to compile a new kernel is located at:

    /usr/src/linux-2.4.31/.config

  6. Notice the dot-file naming convention of the configuration file. The dot-file naming convention means that normally the file is hidden from view within most file management tools.
  7. One area where many people initially stumble is not preserving the current loadable modules in case problems arise in the compiling process. Invariably problems do arise for inexperienced people. There are two approaches to avoiding problems:
  8. Backup the /lib/modules/2.4.31 directory. This is a good idea even if using the next method. This is the method used in this guideline.
  9. Create a new kernel with an embedded extra version number. This extra version number is defined within the associated Makefile, not by the external file name. The external file name is not related to the internal embedded extra version number.
  10. The kernel looks for loadable modules based upon the kernel version that is embedded directly into the binary. During compiling that version number is defined through the Makefile. This is important to remember because the embedded version number need not match the external file name. To the executable kernel code, the only meaningful version number is the number embedded when compiling. The embedded version number determines where the kernel looks for the loadable modules, not the external file name.
  11. The embedded version number can be further modified using an extra version number. Using this method eliminates certain issues and potential obstacles, but there is a notable caveat associated with using the embedded extra version number. If using this method not only do you recompile the kernel, you also must recompile and rebuild your ALSA modules package. Actually, you need to recompile any external package in which kernel modules are stored in /lib/modules. The reason is the manner in which the kernel looks for the loadable modules. The kernel looks for those modules in a directory based upon the embedded version number. If you try merely to copy the sound modules directory to the new modules directory, when you start ALSA you will receive a kernel-module version mismatch error. Therefore, for many beginners, using the embedded EXTRAVERSION number is beyond the scope of this guideline.
  12. Compiling a new kernel on older computers is slow. Using a basic Pentium II system as an approximate baseline, compiling will require about two and one-half hours total time. Many people who regularly compile kernels do so with fast hardware or with a second box that does not hinder using their primary production box. Yes, in GNU/Linux users can run background processes and even log in at a different console, but the compile process still consumes CPU bandwidth. If compiling with only one computer available, understand that compiling is faster without the burden of user interference. With faster computers this is not a significant issue.
  13. If you are curious about the time required to perform each step of the compiling process, prefix each make command with the time command. When the make command terminates the time command will inform you how long the make command lasted.
  14. If you become comfortable at compiling you could write a script to perform the compiling steps. Alternately, many people concatenate the several make steps by using the && symbol. For example, make dep && make clean && make bzImage && make modules.
  15. Within the context of this guideline, running the make modules_install command will delete the sounds directory in /lib/modules/2.4.31. That means either having a backup of that directory handy or reinstalling the ALSA package.
  16. If you are compiling kernels for different CPUs within your network or personal use, then when renaming the new kernel consider adding the CPU type as an extra version suffix. For example, the stock Slackware kernel is compiled for an 80486 CPU. If you want to compile specifically for a 686, then consider a kernel name of something like vmlinuz-ide-2.4.31-686-1. For consistency rename the associated System.map and configuration file likewise, as well as the /lib/modules/2.4.31-x directory. This kind of methodical effort will help later when compiling for several different versions of the kernel.

Creating a Software Compiling Work Area

Browse many of the guidelines posted on the web for compiling a Linux kernel and most authors instruct users to work in the /usr/src/linux directory. This certainly works well, but requires root privileges. For many people with home computers, that is a simple task of logging in as root or changing the user (su) to root. For networked users that might not be so easy a task. Regardless of user permission issues and the philosophy of running as root, there is the risk of clobbering these original source files and making a huge mess of a stock system. A better method is to create a compiling work area accessible in non-root space where mistakes are more easily remedied. This method retains the original kernel source files untouched.

Having a dedicated work area for kernel compiling is handy for compiling other software packages too.

First decide where you have available drive space. Many people can work directly in their home user space (~/). Another approach is to presume or pretend there are additional users who might want to experiment with compiling kernels or other software packages. Therefore a work space in a shared directory is more feasible.

When I first configured my non-production box, I created a large /home partition (8 GB). In that partition I created a directory/folder called public. I use that space as both a local and network shared directory. I also created a directory called /home/builds and in this directory I perform my compiling. In compiler parlance this directory is called a build directory.

To support this concept of compiling software, I created a user group called compilers. My normal login account is a member. In a business or even home LAN, many other users would not be a member of this group.

With that introduction in mind, here are the basic steps to create a work area for compiling software. Adjust and revise for your own system and preferences.

  1. With root privileges create a new user group called compilers.
  2. To this new group add your any user accounts for users who will be compiling software while using your box or in a collaborative effort in a networked environment.
  3. With root privileges create a directory to store source code and compiled software. For my purposes I created the following directory structure:

    /home/builds

    /home/builds/other

    /home/builds/kernels

    /home/builds/kernels/2.4.31

  4. With root privileges modify the file permissions of /home/builds to 774 (rwx-rwx-r--). Assign the normal user account as the individual owner and the group owner as compilers. Replicate these permissions to all subdirectories too.
  5. As a side note, if you decide you do not want the builds directory to be readable by people connecting to your compiling work area, such as on a network, merely disable all world file permissions (770, rwx-rwx--). Assigning such file permissions makes sense if security might be an issue.
  6. With root privileges create a backup of the original /lib/modules/2.4.31 directory. I named the backup directory /lib/modules/2.4.31-orig.
  7. With root privileges create backups of the kernel and configuration files located in /boot.
  8. With the normal user account copy all files in /usr/src/linux-2.4.31 to /home/builds/kernels/2.4.31.

This is the basic method I used to create a work area for compiling software. With this configuration I need not login as root or obtain root privileges to build and compile kernel software. For my small home office network this is not a critical issue, but on a controlled LAN this setup allows compiling without root privileges.

I also have a work area for compiling software source code I obtain from other packages. When reading most examples for compiling other software, the authors typically instruct users to install the source code package into a temporary directory such as /tmp. However, using /tmp is not plausible if that directory is mounted noexec, a common mounting option. This process of creating a dedicated work area provides me a place to compile all software, not just the kernel.

Do notice, however, that after compiling and building new software, to update the binaries and update/install the revised kernel modules you’ll need root privileges.

We’re now ready to start compiling a new kernel. The example provided here presumes a separate work area as just described as well as following the prerequisites and precautions already discussed.

Procedure

For this guideline I do not use the embedded extra version number. I use a manual extra version number with respect to file and directory names, but I do not use the embedded version number that gets compiled into the kernel executable code.

  1. If you want to use the embedded extra version approach then be forewarned that using this method requires additional compiling of all external software packages using kernel modules, such as ALSA.
  2. Using the embedded extra version number method is provided as information only. In this guideline I will not be using this method.
  3. Using a text editor open /home/builds/kernels/2.4.31/Makefile.
  4. At the top of the file modify the EXTRAVERSION field. What information you enter here is for you to decide, but keep things simple. If this is your first attempt at modifying the stock kernel, then use something like EXTRAVERSION = -1. Or, if you are compiling for a specific CPU, use something like EXTRAVERSION = -i686-1.
  5. Notice the leading dash! The make utility uses all text to the right of the equal sign, which means if no dash is typed then the make utility will not add a dash to file and directory names. Thus, without the dash the new file and directory names will be 2.4.311, not 2.4.31-1 or 2.4.31-i686-1.
  6. Save the modification and exit the text editor. Later when you install the newly created modules, the new modules automatically will be stored in a directory named /lib/modules/2.4.31-1 and the kernel files renamed similarly with the additional information.
  7. Create an additional boot loader menu option to support the upcoming new kernel image. Creating an additional menu option allows testing the new kernel while preserving a means of booting with the original kernel should things go awry. Edit the boot loader configuration file. Create a new boot loader entry by copying and pasting the current default menu option. Then edit the new menu option (the following example is for GRUB):
  8. If necessary or desired then exit X.
  9. As normal user, change the current directory:

    cd /home/builds/kernels/2.4.31

  10. Verify an existing copy of the kernel configuration file in current directory. The file will be named .config. If no file exists, then check the original source directory at /usr/src/linux-2.4.31. If no file exists then check the /boot directory. If no file exists in /boot, then check the original distro CDs or ISO images. Otherwise you have to create the configuration file from scratch. However, an original configuration file should exist somewhere if you are using a stock GNU/Linux distro.
  11. Clean house. Many people skip this step, but perform the step regardless. You have two options. You can perform a basic cleaning of old files or a more thorough cleaning. The latter effort also deletes the local configuration file. The latter option is recommended if things get really fouled. When successfully compiling kernels the first option usually is all that is necessary to start a new compiling session.

    make clean (basic file cleaning)

    or

    make mrproper (thorough file cleaning, including the config file)

  12. If you prefer compiling with an existing configuration file, then copy the original kernel configuration file to the current working directory. Notice that the name of the current kernel depends upon your setup. In the /boot directory, often the current configuration file is soft linked and will be named config. You could copy that file too. Just ensure the new file in the local working directory is using the dot-file naming convention.

    cp /boot/config-ide-2.4.31 ./.config

  13. Make a backup copy of the config file within the local directory:

    cp .config config-ide-2.4.31-i486-orig

  14. Notice the backup file does not use the dot-file naming convention. That is to avoid having the backup file being deleted the next time you run make mrproper. The naming convention helps remind you of how you used that particular configuration file. Should you later want to compile another kernel using those configuration options, all you need do is copy that file to ./.config and you are ready to go.
  15. If you are using a configuration file from a previous kernel version, then next ensure the file is current with the newest kernel modifications and Makefile. When you run this next command you likely will be asked some on-screen questions about how you want to configure new kernel options. You need perform this command only when using a configuration file from a previous kernel. For example, from version 2.4.31 to version 2.4.32. If you are modifying a previous configuration from the current kernel then you need not run this command. Note: I have had mixed success using this command. Often I experience various depmod issues.

    make oldconfig

  16. Next modify the kernel parameters. The following step starts the configuration front-end tool. You must run this tool even if you are making no changes to a previous configuration file and are merely updating the new kernel with your previous options:

    make menuconfig

  17. For people attempting this task for the first time, modify only a handful of parameters. The first parameter you might modify is the CPU type. For example, if the default CPU type is for a 486, you might modify that for an i586/i686 box:

    • Select Processor type and features
    • Select Processor family
    • Select 586/K5/5x86/6x86/6x86MX
    • Select Exit
  18. If running a simple home computer, consider disabling RAID and Multiple Disk support:

    • Select Multi-device support (RAID and LVM)
    • Disable Multiple devices driver support (RAID and LVM)
    • Select Exit
  19. Next consider disabling Fusion MPT device support:

    • Select Fusion MPT device support
    • Disable Fusion MPT (base + SCSIHost) drivers
    • Select Exit
  20. Next consider disabling Watchdog cards:

    • Select Character devices
    • Select Watchdog Cards
    • Disable Watchdog Timer Support
    • Select Exit
    • Select Exit
  21. Strongly recommended, next consider enabling the useful Alt-SysRq key sequences to safely reboot a locked computer. Yes, contrary to myth, GNU/Linux boxes do lock-up or freeze. Using the Alt-SysRq key sequences will help safely shutdown and reboot a locked system.

    • Select Kernel hacking
    • Enable Kernel debugging = Y
    • Enable Magic SysRq key = Y
    • Select Exit
  22. Would you like to eliminate that pesky boot error message mentioned previously by embedding scsi emulation directly into the kernel? Toggle SCSI support from a loadable module to built-in:

    • Select ATA/IDE/MFM/RLL Support
    • Select IDE, ATA and ATAPI Block devices
    • Enable SCSI emulation support = Y
    • Select Exit
    • Select Exit
  23. That’s enough modifications for this example. Now Select Exit. Be sure to save the changes!
  24. The next step, as the make command informs on screen, is to create kernel dependencies. This requires a couple of minutes with an older box:

    make dep

  25. The next step does some house cleaning and happens quickly:

    make clean

  26. Next build the new kernel image. This requires about 45 minutes on older PII boxes:

    make bzImage

  27. If you want to capture error and warning messages, then modify the previous command:

    make bzImage 2> ./errors-bzImage.txt

  28. Did the kernel compile? There should be a file named bzImage located in /home/builds/kernels/2.4.31/arch/i386/boot.
  29. Next build the associated kernel modules. This requires about 2 hours on older PII boxes:

    make modules

  30. If you want to capture error and warning messages, then modify the previous command:

    make modules 2> ./errors-Modules.txt

  31. Next create the modules as a complete directory tree. This step requires about two minutes on old computers. Do not create this directory tree within the local kernel source directory. Create the files outside the local directory. I prefer one level up in my builds directory. Our current directory location is /home/builds/kernels/2.4.31. I create the new modules one level up in /home/builds/kernels.

    make modules_install INSTALL_MOD_PATH=/home/builds/kernels/modules/2.4.31-i586-1

  32. This command will create a new directory called /home/builds/kernels/modules/2.4.31-i586-1/lib/modules/2.4.31. Notice the underlying directory structure is exactly the same as what will be installed at the actual modules directory in /lib/modules. This will make copying that directory tree easy, including to other computers. Notice too that although we instructed the make modules_install command to install to a directory using the -1 convention, the command itself does not because the new directory name is based upon the internal kernel name and we did not use the embedded EXTRAVERSION naming method.
  33. If you want to capture error and warning messages, then modify that last command:

    make modules_install INSTALL_MOD_PATH=/home/builds/kernels/modules/2.4.31-i586-1 2> ./errors-Module_Install.txt

  34. The Makefile contains instructions to perform a dependency validation and create a modules.dep file. However, if you want a warm and fuzzy feeling knowing that your compile succeeded, then use the following command:

    /sbin/depmod -a --basedir /home/builds/kernels/modules/2.4.31-i586-1

  35. If all went well there will no error messages. If not, then repeat the previous steps after determining the cause of the errors.
  36. At this point you are ready to update and install the new kernel files and modules. You need root privileges to perform all updating.
  37. You already should have made a backup of the original kernel modules. If not, then do so now, Remember, you need not perform this backup if using the embedded EXTRAVERSION option because all file and directory naming is automated.

    cp -Rp /lib/modules/2.4.31 /lib/modules/2.4.31-orig

  38. Next delete the current kernel modules:

    rm -Rf /lib/modules/2.4.31

  39. Copy the new kernel modules from your working directory to the system /lib/modules/2.4.31 directory.

    cp -R /home/builds/kernels/modules/2.4.31-i586-1/lib/modules/2.4.31 /lib/modules/2.4.31

  40. Next restore the ALSA sound modules. If you did not use the embedded EXTRAVERSION option, then to restore the ALSA sound card modules, copy the /lib/modules/2.4.31-orig/kernel/sound subdirectory from your backup directory. Or you can reinstall the ALSA drivers package. In Slackware, use the installpkg tool, or upgradepkg --reinstall:

    cp -R /lib/modules/2.4.31-orig/kernel/sound /lib/modules/2.4.31

  41. If you used the EXTRAVERSION option to compile your new kernel image, then recompile and rebuild the ALSA drivers package and install that new package. Doing this is beyond the scope of this guideline.
  42. Next copy the new kernel, config file, and system map to the /boot directory. There are two methods to perform this task: automatic and manual. The automatic method is to use make install, but this can be dangerous for inexperienced compilers. The make install command copies the three new kernel files to the /boot directory. If using the embedded EXTRAVERSION option then this option works well because the files all were automatically named using the embedded version number. No previous files are overwritten because of the different file names. However, if using the manual version number, as done within this guideline, you want to maintain the old working kernel for potential recovery purposes. That is another reason why you previously created a new boot loader menu option—so you have a way to boot into your system should the compile effort fail. Therefore manually copy or move the new kernel files. Furthermore, you need to copy the files with file names different from the original stock file names or you will overwrite the original files.

    cp ./arch/i386/boot/bzImage /boot/vmlinuz-ide-2.4.31-i586-1

    cp ./System.map /boot/System.map-ide-2.4.31-i586-1

    cp ./.config /boot/config-ide-2.4.31-i586-1

  43. Create a new working directory backup of your new kernel .config file:

    cp .config config-2.4.31-i586-1

  44. Update the file permissions of the new files:

    chown -R root:root /lib/modules/2.4.31

    chown -R root:root /boot/*-ide-2.4.31-i586-1

  45. As you have already edited your boot loader menu options, now reboot and select your new kernel from the menu list. Hopefully the system boots without incident.
  46. To verify there were no issues with rebooting, check your logs at /var/log. Check dmesg, messages, and syslog, and possibly errors if any similar log exists.
  47. If the reboot went well, there were no error messages, and no depmod issues, and if not using the embedded EXTRAVERSION option, then create a backup of the new modules directory. For consistency, use the same extra version number used to manually rename the kernel. In this example, because I manually created a new -1 version, use the same naming convention:

    cp -Rp /lib/modules/2.4.31 /lib/modules/2.4.31-i586-1

  48. Later, if confident of your success, you could remove the older boot loader options from your menu, but you should maintain the original stock versions of the kernel and modules files—in case of a catastrophe.
  49. If you have aggressively disabled unnecessary kernel features, you might notice some depmod errors after copying the ALSA sounds directory. You can delete the module files that no longer apply.

Recovery From a Failed Compile

The foremost concern of inexperienced software compilers is recovering from an unsuccessful compile. Recovery is a straightforward task if diligent about maintaining consistent file and directory naming conventions. If using the embedded EXTRAVERSION option, this consistency is automated because the extra version number is embedded directly into the software code and file and directory names. If using a manual extra version option, as done in this guideline, then maintaining that same consistency allows for quick recovery.

Consider that regardless of the extra version method used, the following original files exist in /boot:

config-ide-2.4.31

System.map-ide-2.4.31

vmlinuz-ide-2.4.31

After compiling the modified kernel, the following new files exist in /boot:

config-ide-2.4.31-i586-1

System.map-ide-2.4.31-i586-1

vmlinuz-ide-2.4.31-i586-1

If using the embedded EXTRAVERSION method, after compiling the /lib/modules directory contains the following directories:

2.4.31

2.4.31-1

2.4.31-orig

Realize that the 2.4.31 and 2.4.31-orig directories are the same. However, because the EXTRAVERSION number is embedded, the kernel is compiled to use the 2.4.31-1 directory to load modules. This is automated by design. The kernel ignores the 2.4.31 directory.

If using the manual extra version method, after compiling the /lib/modules directory contains the same directories:

2.4.31

2.4.31-1

2.4.31-orig

However, realize with this method the 2.4.31 and 2.4.31-1 directories are the same and both are different from the 2.4.31-orig directory. The kernel was not compiled with the embedded EXTRAVERSION option, so the kernel still loads modules from the 2.4.31 directory.

With either method recovering from a failed compile is straightforward. If using the embedded EXTRAVERSION method there are no backups to restore. Merely use the boot loader option for the previously successful kernel version.

With the manual extra version method, some manual restoration is necessary:

rm -Rf /lib/modules/2.4.31

rm -Rf /lib/modules/2.4.31-1

cp -Rp /lib/modules/2.4.31-orig /lib/modules/2.4.31

Then use the boot loader option for the previously successful kernel version.

Because I created a work area to compile the kernel, the original /usr/src/linux-2.4.31 directory remains untouched and is never a concern. With respect to the kernel source code, recovering from a failed compile is as easy as executing a make mrproper and creating a new configuration file. In a worst case scenario, delete all files in /home/builds/kernels/2.4.31 and copy the original /usr/src/linux-2.4.31 files.

Caveat: When copying the updated module files to another computer, be sure not to copy the /lib/modules/2.4.31/build soft link. That target for that link—your work area directory—likely does not exist on other systems.

Final Comments

Probably one of the most frustrating aspects of compiling a kernel is failed module dependencies. As I remarked at the beginning of this guideline, I do not pretend to be an expert on kernel or software compiling. However, a dependency issue means just that—a required module is missing and the reason is the module was not compiled. That is, a feature you selected within the kernel configuration is dependent upon another feature that you did not enable. The best solution is to think and visit the online forums to better understand why the compile process failed with certain modules.

If your first kernel compile succeeded, then likely you will be attempting additional modifications to the kernel and compiling again. If using the embedded EXTRAVERSION option, and not minding recompiling those additional software packages using kernel modules, then updating and preserving previous /lib/modules/2.4.31 directories is always an automated process, as well as being able to use the make install command to install the new kernel to the /boot directory. Using the embedded EXTRAVERSION option does promote a certain ease of use and updating, and all file and directory naming is automated by the Makefile. The one caveat with this approach is recompiling all of those external packages using kernel modules.

A majority of people, however, do not want to recompile those additional packages. They should use the manual version numbering process as used in this guideline. The one caveat with this method is manually backing up previous working modules. Therefore, after each successful compile and reboot, be sure to copy the newly updated /lib/modules/2.4.31 directory to /lib/modules/2.4.31-x, where x represents the new version number being used manually.

I hope this guideline helps you!

Additional Reading

Kernel Rebuild Guide

Finis.

Table of Contents