How to build and install your own Linux kernel

From LQWiki
Jump to: navigation, search


Introduction

This guide to building and installing your own kernel is intended for those who:

  • want to use a distribution that expects users to build their own kernel
  • want to make their kernel smaller and faster to load
  • want to avoid having to use an initrd image for booting
  • want to test or fix a kernel configuration issue
  • need the newest kernel to run their hardware
  • are simply curious.

There was a time when Linux users in general were expected to build their own kernels. Nowadays most users use the stock kernel that came with their distribution. Modern computer processors (CPUs) run fast, so it is no longer necessary to shrink the kernel simply to save on boot time. However if you do need or want to "roll your own", this article will guide you through the process.

Prerequisites for the build

Prerequisites for your system

To build any software, you need a set of tools for the purpose such as

  • a compiler to create the binary code
  • tools for linking, indexing and otherwise handling the resultant binaries,
  • informative headers for the various libraries that you wish to link to; the compiler needs these to ensure that the program is handling library functions correctly
  • a way of selecting and building in the many optional configuration variables that the developer has provided for you

and so forth. GNU provides a standard set of such tools for this purpose which is known as the GNU build system.

A full list of the build tools required might be rather offputting. Fortunately most Linux distributions either include these tools in a standard install, or provide them in a single installable package. For example, all distributions in the Debian family (such as Ubuntu) have a package called build-essential, which contains everything you need for most builds. Other distribution families have similar packages such as Arch's base-devel. Some distributions also have something called "package groups" that can install all of the required development tools for you, if your distribution has such an option, you can look for something called "development" or similar. A quick search of what your distribution provides should tell you the name of any package(s) you need to install separately.

In addition to the basic build tools, the kernel configuration dialogue requires the development package (headers) for the ncurses library, and modern kernels require the following build dependencies;

  • xz-utils
  • libssl (both the library itself and its development package as well)
  • bc
  • flex
  • libelf (both the library itself and its development package as well)
  • bison

Note: Some of the above packages may already be installed on your system, therefore you should check with your distribution's package manager to ensure all of the packages listed above are in fact installed before continuing on. You should also note that the exact names of the packages listed above may vary slightly from distribution to distribution, so check with your package manager before continuing.

Prerequisites for the user

Users who wish to build their own software (and enjoy the process) need a few basic skills. You need to feel fairly confident working at the command line, which means typing instructions into a terminal rather than using your mouse to click on buttons. You need some basic knowledge of how directories (aka folders) and files are organised in Linux, how to switch directories, how to move or copy files and so on. This wiki provides quite a lot of instruction materials on such elementary matters.

To make your new kernel bootable, you will need a basic knowledge of how your bootloader works and how to add a new kernel to its menu.

Configuring the kernel for building

Arguably this is probably the most important part of the process in building your new kernel, because the way you configure the kernel will determine which drivers and other features your kernel is built with, and whether they are built in or built separately as loadable modules. There are several ways you can configure the kernel for building each with its own advantages and disadvantages. We will list each configuration method below, while listing some advantages and disadvantages of each method.

  • Answering all questions from the kernel build system for each and every kernel configuration option

This approach, not often used nowadays, allows you to specify exactly which features and modules you would like to build your new kernel with. The disadvantage is that it takes a long time to finish answering all of the questions asked by the kernel build system, and it may require a lot of understanding of which configuration options are most relevant to your machine. Also, if you accidentally send an answer you did not intend, you have to abort the process and start again. You can shorten the time by just pressing return (also called "Enter") where you have no strong preference instead of answering Yes or No. This will give you the default answer to the question, which is often a fairly safe one.

  • Copying the configuration file for your existing kernel

This approach has the advantage of being one of the quickest options. The disadvantage is that you may be building the kernel with modules that you simply don't need and features that you do not want. Also this may not be an option available to you. It depends on your system and whether or not you have an existing configuration file.

  • Using make menuconfig

The advantage with this is that it will provide a text-based menu where you can configure each kernel configuration option by hand, thus giving you complete control over which features and modules you build the kernel with. You can also use this approach starting from your current kernel's configuration file. The disadvantage is that it may still be quite tedious and time-consuming. Also the menuconfig interface, based on the ncurses library, is not particularly friendly for people accustomed to a graphical interface.

  • Using make xconfig or make gconfig

If you have a KDE desktop or at least the Qt libraries, you can use make xconfig for a graphical configuration window. If you have the gtk libraries (Gnome or xfce desktops), use make gconfig instead. The advantages and disadvantages of a graphical approach are much the same as for make menuconfig. However, the interface is easier to navigate, as you can use your mouse to click on options.

  • Using a "kernel seed"

This approach is essentially a pre-written kernel configuration file that includes options for a particular type of system. Basically a lot of the work has already been done for you. The disadvantage is that you would need to make sure the configuration is suitable for the target system.

  • Using make oldconfig

This is the method you use if you have already built a kernel and you simply want to upgrade it to a new version. Before using it, you must first copy the old configuration file to the Linux-x.y.z build directory as .config. The script will ask you questions only about new configuration options that have no equivalent in the old file. This is usually a quick and easy way to build a kernel, but it becomes progressively more burdensome as the gap between the old kernel version and the new one grows wider. You should not use it to upgrade a very old kernel.

The first thing you need to do once you have prepared your system for building the kernel is to download the kernel source, which you can find at http://www.kernel.org. The source downloads as a "tarball", a compressed archive which must be decompressed using the tar command. This should be done in your home directory/folder, where you have full rights to read, write and create files. It will create a toplevel directory called Linux-x.y.z, where x.y.z is the version number. Change to this directory by using the cd command. Make sure that you are working as an unprivileged user and not as root.

The kernel build system will look for a file called .config that will reside in the directory you are now in, so copy the configuration file you are going to use as a starter into this directory, naming it .config (note the initial dot in the filename which makes it a "hidden file"). This will be the file that is edited by the configuration process. It is also the file that the make command uses to know what kernel configuration to use when actually building the kernel. It is important to note that the process of configuration overwrites the existing .config file, so if you want to make any manual changes to this file, do not do so until after you've finished the configuration.

Notes

  • If you do not have a starter configuration file to copy, you can create a .config file with sensible defaults by running the command make defconfig, before using make menuconfig/xconfig.
  • If you are building the kernel using the LLVM/Clang toolchain, there maybe some configuration options that you may need to change for the build to succeed at all. We will cover this in the "Tips and tricks" section of this wiki page below.

The build step by step

Now that we have configured our kernel build, it's time to start building it. Be aware that this step can take quite some time to complete depending on how you've configured the kernel in the previous steps. There are some arguments you can use with make to speed up the build quite dramatically described in the "Tips and tricks" section of this wiki page below.

You only need to choose one of the following build options, not both.

Using the GNU toolchain for the build

Making sure you are still sitting in the kernel source directory, issue the following command as a normal user;

make

Using the LLVM/Clang toolchain for the build

If you are using the LLVM/Clang toolchain instead of the GNU build system, just running make as described above may not succeed and the kernel build may fail. This is because, at the time of this writing, there seem to be issues with using the built-in LLVM linker for linking the kernel code and modules. You need to direct make to use the GNU linker with the associated BFD libraries to link the compiled code.

Making sure you are still sitting in the kernel source directory, issue the following command as a normal user;

make CC=clang LD=ld.bfd

Installing the new kernel and its modules

If you've reached this step, congratulations! You are now ready to install your new kernel.

The first step is to install the drivers and other modules that the previous step built to their new home on the root filesystem of your system. But we need to do this as the root user. In some distributions (such as Ubuntu), this is done by preceding each command with the word sudo, entering your password if requested. In others, you can simply switch to the root user account. Make sure you are still in your kernel source directory where you previously issued the make command, or the following commands will fail.

To install newly built drivers, run the following command;

make modules_install

Installing the kernel itself

There are a couple of ways you can install your newly built kernel. The easiest way just involves executing a make command and all the work is done for you. You can also install it manually, which is described afterwards.

Choose one of the following methods.

  • To use make to install your new kernel and its associated files, run the make install command. The kernel will be installed under the filename /boot/vmlinuz and any previous file or link of that name will be renamed with a .old suffix. This may not be what you want.
  • Alternatively, simply copy the bzImage file and the system map file by hand to the /boot directory of your system, using:
cp arch/x86/boot/bzImage  /boot/your_chosen_kernel_name (replace x86 if necessary by your actual CPU architecture)
cp System.map /boot/your_chosen_system_map_name

The advantage of installing this way is that you can choose what to call your new kernel image to distinguish it from any existing ones.

Notes

  • If you use make to install the kernel, it will attempt to add the new kernel to the LILO configuration file. If you are using a different bootloader, you will get a warning message that LILO was not found, which can be safely ignored. Please refer to the following section of this wiki page for details about making your new kernel bootable.
  • Whichever installation method you use, it is a good idea to also copy over to /boot (for storage and future reference) the final .config file:
cp .config /boot/your_chosen_config_name

Making your new kernel bootable

The final stage is to make your bootloader aware of your new kernel, so that it can load and run it. You may also need to create an initrd image for this kernel, depending on how you have configured it.

Making an initrd image

You may or may not need this. One advantage of using a handmade kernel is that you can configure it to include all the disk drivers you need, rather than building them as dynamic modules to be loaded at boot time. If you do this, your new kernel will be able to boot directly from the hard drive.

For this to work, you will need to include the basic block device layer, the libata disk driver and the driver for your root filesystem (usually ext4). If you have a SATA or NVME drive, you will need the drivers for the appropriate controller. Make sure that you have said "YES" to all of these so that they compile as static internal code and not as modules.

A kernel which does not have all these drivers built in can still boot successfully, but it will need an initrd (sometimes also called an initramfs image). This is simply a very basic root filesystem stored as a compressed image and subsequently loaded onto a ramdisk by the bootloader. The kernel uses this as an initial root device, loads any driver modules it needs from it and then switches to the real root device on the hard drive.

All distributions include a script for making such a device. It is usually called mkinitrd or mkinitramfs. Use the command's man page to find out how to use it, as these scripts are completely unstandardized. Make sure your initrd image is in the /boot directory alongside the kernel.

Note: If you have an encrypted root partition on your hard drive, you will definitely need an initrd containing the software to decrypt it, no matter how your kernel is configured. But that is beyond the scope of this article.

Adding the kernel to the boot menu

Most Linux systems nowadays boot with GRUB, which works with both traditional BIOS machines and the more modern computers that use UEFI. GRUB is quite a complex program which includes drivers for various filesystems and can therefore find kernels anywhere in your system. Most versions of GRUB come packaged with scripts for automating these processes. The update-grub command (given with root privileges as described above) will usually be sufficient to cause GRUB to find the new kernel and add it to its configuration file. When GRUB next runs, it will use this file to generate a boot menu for you.

Different GRUB installations may manage their menus differently, depending on how they have been scripted. Some produce a single menu containing all the kernels they have found. Others have only two entries in the top-level menu: the most recent kernel and a "Further options" which gives access to a sub-menu containing the other kernels. If you cannot find your new kernel anywhere, the automated scripts have failed to find it and you will need to modify one of the GRUB scripts to include it explicitly. GRUB scripts are kept in /etc/grub.d and should include at least one script named "custom". You can add the path to your kernel (and initrd if you are using one) to this file and then run update-grub.

LILO is an alternative (and older) bootloader which some distributions use with BIOS-booting computers. It has a configuration file called /etc/lilo.conf into which you need to edit the path to your kernel and optional initrd, using an existing entry as a model. Then you must run (as root) a program called lilo which will transfer the configuration details into the LILO map file. The LILO bootloader can then find these files by their actual physical addresses.

ELILO is the LILO equivalent for UEFI computers. It uses a configuration file called elilo.conf which is stored on the EFI system partition, a special vfat-formatted partition for storing boot files. Most distributions mount this partition at need on the /boot/efi directory. Unlike GRUB, ELILO cannot read filesystems other than vfat, so if you are using this bootloader, you will need to copy over your kernel (and initrd if you are using one) to the EFI partition. The syntax for elilo.conf is identical to that for lilo.conf, but there is no need to run an installer program after editing it as you do for LILO.

There is further information about configuring the bootloaders discussed above in the "Tips and tricks" section below.

Note: The update-grub script may not exist in some distributions, refer to the "Tips and tricks" section below for more details.

Tips and tricks

In this section we will provide some useful tips, as well as some solutions to common problems you may run into, both during, and post-build.

Copying your current kernel configuration over to your new kernel build

It may be desirable to copy your current kernel configuration over to your new kernel build for a number of reasons, if this option is available to you.

You can do this in a couple of ways, depending which option (if any) is available on your system. If your distribution installs the kernel configuration into the /boot directory, you can use the command below:

cp -v /boot/config-xxxx .config

You will need to change "config-xxxx" to the actual filename for your current kernel's configuration file that was used to build it with. The above command also assumes that you are currently sitting in your kernel source directory. If you are not, change to this directory, or the cp command will fail to copy your current kernel's configuration over to your new kernel build.

You can also get the kernel configuration used to build your current kernel from /proc with the command below:

zcat /proc/config.gz > ~/kernel_source_directory/.config

You need to change "kernel_source_directory" to the actual directory name of the directory where your kernel source has been decompressed to earlier on.

Note: The -v argument means "be verbose" as by default the cp command will only display what's happened if it encounters an error, but this argument is not explicitly required.

LLVM/Clang build issues

If you are using the LLVM/Clang toolchain to build your new kernel, you may run into the following error during the build process;

    BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
    Failed to generate BTF for vmlinux
    Try to disable CONFIG_DEBUG_INFO_BTF
    make: *** [Makefile:1170: vmlinux] Error 1

Thankfully, there are a couple of simple solutions to this issue. You can either install the package containing pahole or you can disable that particular kernel configuration setting as described below.

If you cannot install the pahole tool, you need to change the following line (under the "Compile-time checks and compiler options" heading in your kernel configuration file) from;

CONFIG_DEBUG_INFO_BTF=y

to

CONFIG_DEBUG_INFO_BTF=n

or simply comment out the above line altogether with the hash symbol (#) appended to the very beginning of the above line. Thankfully, you should not need to start over, and therefore you can use any text editor to edit your kernel configuration file. Then simply issue the same make command you used prior to that error message appearing, to continue building your new kernel. The build should continue on from the point at which it stopped.

Note: If you plan on using the LLVM/Clang toolchain, you can avoid this issue by changing the kernel configuration option described above when you configure your new kernel prior to building.

Speeding up your new kernel build

Building your own kernel can take quite some time to finish, but its possible to reduce this time even where you have included a lot of modules. The make command has an argument that can dramatically reduce the time it takes to finish building the kernel and its modules. This argument is the -jX argument make provides, where X is the number of "jobs" make should execute concurrently.

In its basic form you can use the following example;

make -j5

You can change -j5 to some other number instead. A good rule of thumb is to use the number of cores in your CPU.

A word of warning though: using this argument can put even the most powerful system under a lot of strain. It is therefore strongly recommended that you do not perform any other tasks while the kernel build is taking place, particularly those that are highly intensive. You may overload your system, triggering the running kernel's "out of memory" response and crashing your kernel build in the process.

It is beyond the scope of this article to list all arguments make has to offer. You can look at the make command's man page for further details. You can also use make help to get a list of all the specific targets for kernel make beyond those described here.

Manually adding your new kernel to your GRUB boot menu

If you are using GRUB and the GRUB scripts fail to autodetect your new kernel, you can still manually add the relevant information to /etc/grub.d/40_custom. As the root user, open this file in a text editor and add something like:

       menuentry 'kernel x.y.z' {
       echo 'Loading Linux x.y.z ...'
       linux /boot/vmlinuz root=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx <kernel parameters>
       echo 'Loading initial ramdisk ...'
       initrd /boot/initrd
       }

You need to change "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" to the actual UUID of your root partition, and replace "<kernel parameters>" with valid kernel parameters appropriate to your setup. If you are not going to use an initrd, replace the UUID with the partition's device name (e.g. /dev/sda2) as the kernel cannot read UUIDs without a working root partition.

The most important lines are the lines beginning with "menuentry", "linux" and if you are using one, "initrd". So let's give a quick explanation of what they do; The line beginning with "menuentry" signals the start of a menu entry and gives the name that will appear in the menu itself. The line beginning with "linux" tells GRUB where your kernel executable is located on the filesystem, where your root filesystem is located, along with any kernel parameters you may want to boot the kernel with. The line beginning with "initrd" tells GRUB where to find your initrd image (this line can be omitted if you are not using an initrd). If you're not using an initrd use "root=/dev/XXXX" (where "XXXX" is the device node of your root partition) instead of "root=UUID=" in the line beginning with "linux" above.

Once you have added the relevant information, you then need to update your GRUB configuration so the changes take effect. We will discuss how you do that in the "Updating GRUB" sub-section below.

Further GRUB boot options may sometimes be desirable, but that is beyond the scope of this article. Please refer to the GRUB documentation if you have any questions about any of GRUB's boot options.

Note: The curly braces are required, otherwise the entry will not be added to your GRUB boot menu.

Manually adding your new kernel to a LILO or ELILO boot menu

These two bootloaders use the same configuration syntax. The main difference is that lilo.conf (used for MBR boots) is stored in the /etc directory on the root partition and elilo.conf (used for UEFI boots) on the EFI system partition. You may need to mount this partition by hand, depending on your distribution. In either case, you must edit the configuration file as root.

   image= /path/to/linux/kernel/  
   label = 'My new kernel' 
   initrd= /path/to/initrd/image 
   read_only  
   append "root=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx <kernel parameters>"

For lilo.conf, all paths should start from /, the toplevel directory of your root partition. For elilo.conf, they must start from the root directory of the EFI system partition, and both the kernel and the initrd (if you are using one) must be present on this partition. The read_only option causes the root partition to be mounted initially read-only, which is good practice. The name of the root partition and other kernel command line parameters are passed in the "append" statement. The label is simply what will appear in the menu.

If you are not using an initrd, omit the initrd line and replace "root=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" with "root=/dev/sda(x)", e.g. "root=/dev/sda2"

For ELILO, nothing further is required. For LILO, complete the operation by running (as root) /sbin/lilo.

Updating GRUB

On some systems, the update-grub script may be called update-grub2. If neither of these is available, you can run the grub-mkconfig (or grub2-mkconfig as it may be called) executable directly with the relevant arguments as shown below.

grub-mkconfig -o /boot/grub/grub.cfg

Note: You should never edit /boot/grub/grub.cfg directly, as this file will be overwritten by any software update that involves updating GRUB.

Using contextual help in kernel configuration

Like many programs, the kernel configuration script provides contextual help. In fact kernel help is an unusually complete and informative system and you should get used to using it intensively.

If you are using menuconfig, you can get contextual help at any time by pressing the H key. In xconfig, click on the Help button with your mouse. The help you get will always be appropriate to the currently selected option.

Most contextual help systems simply tell you what an option does. Kernel help often tells you whether you need that option or not. It may tell you, for example, "If in doubt, say Yes" or "You will only need this option if you are using (such-and-such a device)", or even in some cases, "If you don't know what this is, you don't need it."

Sometimes a piece of software needs to be run with a kernel that has certain configuration options set. This is indeed one reason why people sometimes need to rebuild their kernel. The relevant documentation will always give the form the option takes in the actual configuration file. It is easy enough to find out if your current running kernel has this option set by using grep to search the saved file, which is why .config is normally saved in the boot directory for future reference. But if the option has not been set, how do you find it in the large and complex kernel configuration menu?

Fortunately another kind of help has been provided by the kernel development team. Simply type a / character and a text box will appear into which you can type the name of the option. Press return (also called "Enter"), and you will be taken at once to the part of the menu system which allows you to configure that option.

See also