Recompiling the Linux Kernel

One of the beauties of free and open-source software like Linux is that, since you have the source code, you can recompile the software to suit your own requirements. In fact, many Linux users build their own kernel from the source code.

Why?

You can obtain worthwhile performance improvements, or cut down the size of the kernel for use on smaller systems. You can also run a more up-to-date version of the kernel than the one that is supplied with your original Linux distribution - for example, at the time of writing, Red Hat is shipping a 2.4.20 kernel, but the latest stable version is 2.4.21, and of course, the development, or beta, series of kernels is up to 2.5.75. This means you can take advantage of new features, bug fixes or security fixes before your distro vendor responds.

However, even without actually rebuilding the kernel, you may need to have the kernel source installed - and correctly configured - on your system in order to be able to install various drivers. In the last month alone, I've had to configure, but not compile, the kernel source in order to be able to install drivers for nVidia nForce hardware, a Promise RAID controller, a wireless network card and other hardware devices.

Rebuilding the kernel source is also a worthwhile exercise for the insight it provides into how the Linux system works - you'll gain a new understanding of how modules are used, how the system starts up, and other useful information,

But the main reason for recompiling the kernel is probably just bragging rights - it's one of those things you have to do before you're a real Linux user!

The Steps

The first thing to do is to install the source code for the kernel. If you simply want to use the source that came with your distribution, you will find it on the CD-ROMs, typically as an RPM package. For example, Red Hat 9 includes the source on CD 2, as kernel-source-2.4.20-8.i386.rpm, and you can install it with the command:

rpm -ivh /mnt/cdrom/RedHat/RPMS/kernel-source-2.4.20-8.i386.rpm

Notice thatyou'll need a lot more than just the kernel source package - you'll also need various development tools such as gcc (the C compiler), make, and optionallyTcl/Tk. If you haven't already installed these, you should do so - on Red Hat, the simplest way is to use the redhat-config-packages command, and select the "Development Tools" and "Kernel Development" package groups.

If you want to download a generic kernel, you can obtain the source package from The Linux Kernel Archives at http://www.kernel.org/ . The first time you do this, make sure that you download the full kernel source tarball - for serious users each new version is also made available as a collection of patch files which will update the previous version to the new one (much faster to download, you see), and this is displayed as the first column on the home page at www.kernel.org. You are looking for a file called linux-2.4.21.tar.bz2 or linux-2.4.21.tar.gz (or similar), which is under the "F" link at the end of the line.

Once you have downloaded the source tarball, you should untar it and install it. You can do this in several ways, in several locations, but I've always done it in /usr/src/linux. The kernel README file contains a dire warning against this: "Do NOT use the /usr/src/linux area! This area has a (usually incomplete) set of kernel headers that are used by the library header files. They should match the library, and not get messed up by whatever the kernel-du-jour just happens to be". However, on Red Hat, the library kernel headers are in /usr/include/linux, and get installed from the glibc-kernheaders RPM. In short, I've never had any problems with building the kernel in /usr/src/linux, and most other people do it this way, too.

In general, the source will untar itself into a directory called linux. It's common practice to have several source trees installed simultaneously, with a symbolic link called linux that points to the current one. So, to install the source, use the following commands:

cd /usr/src
mkdir linux-2.4.xx
rm linux
ln -s linux-2.4.xx linux

If your source code tarball is a gzipped tar file, then the next command is:

tar xzvf /path/to/linux-2.4.xx.tar.gz

If it is a .bz2 file, then use the command:

bzcat /path/to/linux-2.4.xx.tar.bz2 | tar xvf -

The final step in installing the source tree is to clean up any stale files:

cd linux
make mrproper

The EXTRAVERSION String and Module Files

There's an extra complication for those of you who want to customise the kernel you are currently running, or who want to compile driver modules to load with an existing vendor-supplied kernel. When you compile modules for a specific kernel, they are installed into a subdirectory tree that is specific to that particular kernel. For example, if you are using Red Hat 9 with its stock supplied kernel 2.4.20-8, the modules go into a directory called /lib/modules/2.4.20-8/, specifically into a directory called kernel under there. If you now build a 2.4.21 kernel, then its modules will be installed into /lib/modules/2.4.21/kernel. No problem there.

But what if you were to recompile the supplied kernel version with your options? Now your modules will also be installed under /lib/modules/2.4.20-8/kernel, overwriting the files that were previously there. If you've screwed something up, you could select the previous kernel at boot time, but its modules are gone, and it might no longer be bootable.

What's needed is a different name for the modules directory - and since that is produced during the build process, it really means a different version number for the kernel. The version number is set inside the Makefile (/usr/src/linux/Makefile) by some lines at the top. For a stock Red Hat 9 kernel, it starts out like this:

VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 20
EXTRAVERSION = -8custom

This means that when you build your new kernel from the Red Hat-supplied sources, the kernel version will be 2.4.20-8custom, and your modules will be installed in /lib/modules/2.4.20-8custom/ so that they won't interfere with the existing modules in /lib/modules/2.4.20-8/

You can set the EXTRAVERSION string to whatever makes sense for you - it's usually used to indicate the number of vendor-applied patches.

But, if you specifically want to build third-party (e.g. nVidia nForce or Promise FastTrak) modules to load with the supplied Red Hat kernel, you will need to edit the EXTRAVERSION string by removing the "custom" part. If you don't do this, your generated modules will be installed in the wrong location, will be compiled against the wrong kernel version and won't load.

Kernel Configuration

Configuring the kernel means specifying a lot of different options:


And so on, and on, and on. There are hundreds of options, many relating to support for hardware such as SCSI and RAID controllers, flash devices, as well as network options, filesystem options and the like. Then there are the device drivers - for network cards, ISDN cards, serial cards, audio cards, and so on, each of which can be compiled either directly into the kernel or as a module for loading after the system has booted.

A full description of all the configuration options is really beyond the scope of this article - however, there is online help which describes each option.

The kernel configuration options are stored in a file called ".config". There are four ways of producing this file: make config, make menuconfig, make xconfig and make oldconfig.

make config: This command asks about each option in turn. Most questions can be answered with Y or N

[root@sleipnir linux]# make config
rm -f include/asm
( cd include ; ln -sf asm-i386 asm)
/bin/sh scripts/Configure arch/i386/config.in
#
# Using defaults found in arch/i386/defconfig
#
*
* Code maturity level options
*
Prompt for development and/or incomplete code/drivers (CONFIG_EXPERIMENTAL) [N/y/?]

The script prompts, showing the possible answers - in this case "N", which is the default (shown first in upper case) if you press Enter, "Y", or you could press "?" to get some online help or explanatory text. In the case of device drivers, the question can usually be answered with "Y", "N" or "M" - "Y" compiles the driver into the kernel, while "M" will cause it to be built as a separate module. For example:

Power Management support (CONFIG_PM) [Y/n/?]
 Advanced Power Management BIOS support (CONFIG_APM) [N/y/m/?]

make menuconfig: This command will provide a full-screen menu-driven interface which reads the existing .config file upon startup and allows you to adapt or fine-tune it. Operation is fairly intuitive, with navigation by the arrow and Tab keys, with Enter to select; at the end of the procedure, you are asked "Do you wish to save your new kernel configuration?" and can answer "Y" or "N" as appropriate.

make xconfig: Assuming that the Tk widgets are installed and you are running an X server, this will produce a graphical interface which will allow you to step through the various options (usually by clicking on the "Next" button) or refine an existing .config file by selecting just the desired options.

make oldconfig: This is a refinement of "make config" which lets you take an existing .config file from a previous kernel version and answer just the questions that relate to any new options.

Hints for Configs

It's not a bad idea to stash away a copy of the .config file, once you've got a working and optimized kernel. I save them away on our file server, with a name like hostname-2.4.21-1.config; that way, when I want to build a new customized kernel for a new version of Linux, I just copy the file back to .config and run make oldconfig.

If you want to configure a kernel the same way as your installed Red Hat kernel, you can just

cp /boot/config-2.4.20-8 /usr/src/linux/.config

The Red Hat installation process puts a copy of the .config file that was used to build the kernel you are using into /boot (actually it comes from kernel-2.4.20-8.i686.rpm or whatver kernel RPM package was installed). The configuration files tor other kernel builds are also on your system, in the directory /usr/src/linux-2.4/configs, which contains configs for Athlon, i586, i686 and various SMP kernels.

After you have copied a .config file from somewhere else, you should run "make oldconfig". You won't be asked any questions, but this seems to be a necessary step.

Dependencies

After saving a .config file, or copying one from somewhere else, the next step is to create a dependencies file. This is used by the make process to decide what files depend upon which other files, and which files therefore need to be recompiled. The command is simple, and only takes a few seconds on modern machines:

make dep

If you do not need to build a kernel, but are only configuring it so that you can build third-party modules to work with it, you can leave our story at this point and turn your attention to the steps needed to build your modules. The rest of us can now build the kernel. This is the magic step:

make bzImage

The kernel will be produced as a file called arch/i386/bzImage, which we will copy into the correct location in a later step. But for now, we need to build and install the modules:

make modules && make modules_install

If you haven't seen the && operator before, it links two commands so that the second command will only be attempted if the first one succeeds. This is so that if there is some problem with compiling the modules (the make modules step), then we won't get damaged or old files installed into /lib/modules and no error messages on the screen when we return from that coffee break.

So now the modules are in the right place; it only remains to move the kernel into the /boot directory under an appropriate name and add an entry for it in the boot loader configuration file. You might also need an initrd image.

First, copy the kernel into place (I'm assuming here that you've set the EXTRAVERSION string to "-custom", and that your current directory is still /usr/src/linux):

cp arch/i386/boot/bzImage /boot/vmlinuz-2.4.21-custom

Now, you should copy the system map file into place. This file is not strictly necessary, but without it, a few commands like top, ps and the gdb debugger won't work correctly. In actual fact, I'd usually create a file that was specific to this kernel and then create a symbolic link called System.map which points to the actual file:

cp System.map /boot/System.map-2.4.21-custom
ln -sf /boot/System.map-2.4.21-custom /boot/System.map

If your kernel needs to load some modules in order to be able to access the root filesystem - for example, if you have compiled the ext3 support as a module (ext3.o) and the root filesystem is ext3, or if you are booting from a SCSI drive and the SCSI adapter driver (say, aic79xx.o) has to be loaded from /lib/modules/2.4.21-custom/kernel/drivers/scsi/aic7xxx, you are facing a chicken-and-egg situation: you can't access that drive until the appropriate module has been loaded, and you need to get the appropriate module off the drive. The answer to this problem is to create an Initial Root Disk (or Initial Ram Drive) - the kernel loads a small filesystem image into memory after itself and then loads the required modules from there. This image is created with the mkinitrd script, which reads /etc/fstab and /etc/modules.conf (looking for scsi_hostadapter entries), and will then create an initrd file containing the appopriate modules. The basic syntax is:

mkinitrd image-file kernel-version

but see man mkinitrd for additional information. So, for example, to make an initrd for our 2.4.21-custom kernel to boot and mount an ext3 root filesystem on a SCSI drive:

mkinitrd initrd-2.4.21-custom.img 2.4.21-custom

Once that's been done, it only remains to add the new kernel to the boot loader configuration. Here, I'll discuss GRUB, since that is now used by most distributions, but the principle is similar for LILO.

Edit /boot/grub/grub.conf, and find the entry for your current kernel. Copy the four or so lines for that entry, and edit the copy to reflect the correct title, filenames and options for your new kernel. For example, your system might currently have:

title Red Hat Linux (2.4.20-8)
       root (hd0,0)
       kernel /vmlinuz-2.4.20-8 ro root=/dev/Volume00/LogVol01
       initrd /initrd-2.4.20-8.img

After copying this and editing the copy, you might have:

title Red Hat Linux (2.4.20-8)
       root (hd0,0)
       kernel /vmlinuz-2.4.20-8 ro root=/dev/Volume00/LogVol01
       initrd /initrd-2.4.20-8.img
title Red Hat Linux with 2.4.21 Kernel
       root (hd0,0)
       kernel /vmlinuz-2.4.21-custom ro root=/dev/Volume00/LogVol01
       initrd /initrd-2.4.21-custom.img

With GRUB, you can just save this file, then shut down and reboot (all the while, saying a silent prayer to St. Linus). When the system restarts, select "Red Hat Linux with 2.4.21 Kernel" and watch carefully for any error messages. If this is your first attempt at building a kernel, there will almost certainly be several! You might have forgotten to compile a module that is required by a network card, sound card or other peripheral. A common mistake is forgetting to compile the ext3 support into the kernel, or forgetting to run mkinitrd if you've compiled it as a module.

Make a note of the error messages, and see if you can figure out what you've forgotten. Then reboot, select the old kernel (it's still the default at this stage anyway) and go back to the make xconfig stage to fix up your kernel configuration. Repeat as required.

What About make clean?

You'll sometimes see people recommend that you do a make clean before the make bzImage step. This is usually unnecessary and completely defeats the purpose of using a make utility, which is to only compile those things that need to be recompiled; make clean deletes all object and other output files, thereby ensuring that everything has to be compiled from scratch. This is time-consuming and usually unnecessary - I would only perform a make clean if getting puzzling compile-time errors, especially after making changes to EXTRAVERSION or changing major compile options.

Final Tidy-Up

Edit /boot/grub/grub.conf one last time, and change the default entry to point to your new entry. A value of '0' sets the first entry as the default, '1' sets it to the second, and so on. If you haven't set up the System.map symlink, then do it now. And if you will want to recompile a kernel for this machine in the future, then save the .config file away in a safe place.

That's it! The first few times you build your own kernel, it's quite common to have errors when you try to reboot, but a little thought will usually reveal where you've gone wrong. And once you've done this, you've acquired the first stripe on your Linux Guru badge!

Building a 2.6 Kernel

The build system of the 2.6 kernel has been changed around quite a bit, but the process remains pretty much the same. Here are some differences:


Other than that, it's business as usual.

Tech Terms

source tree - the source code for any reasonably large program is typically organised into an entire directory tree

tarball - the UNIX world's equivalent to a ZIP (Winzip) archive - a collection of files collected into a single archive with the tar (tape archive) utiliity and typically then compressed with the gzip or bzip2 utility


Page last updated: 20/Jul/2004 Back to Home Copyright © 1987-2010 Les Bell and Associates Pty Ltd. All rights reserved. webmaster@lesbell.com.au

...........................