Compiling

The kernel build system, kbuild, is a set of make scripts that take the configuration information from the .config file, work out the dependencies and compile everything that is necessary to produce a kernel image containing all the statically linked components, possibly a device tree binary and possibly one or more kernel modules. The dependencies are expressed in the makefiles that are in each directory with buildable components. For instance, these two lines are taken from drivers/char/Makefile:

obj-y                    += mem.o random.o
obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o

The obj-y rule unconditionally compiles a file to produce the target, so mem.c and random.c are always part of the kernel. In the second line, ttyprintk.c is dependent on a configuration parameter. If CONFIG_TTY_PRINTK is y it is compiled as a built in, if it is m it is built as a module and, if the parameter is undefined, it is not compiled at all.

For most targets, just typing make (with the appropriate ARCH and CROSS_COMPILE) will do the job, but it is instructive to take it one step at a time.

To build a kernel image, you need to know what your bootloader expects. This is a rough guide:

Here is an example of building a zImage file:

It is the same when building bzImage and uImage targets.

There is a small issue with building a uImage file for ARM with multi-platform support, which is the norm for the current generation of ARM SoC kernels. Multi-platform support for ARM was introduced in Linux 3.7. It allows a single kernel binary to run on multiple platforms and is a step on the road toward having a small number of kernels for all ARM devices. The kernel selects the correct platform by reading the machine number or the device tree passed to it by the bootloader. The problem occurs because the location of physical memory might be different for each platform, and so the relocation address for the kernel (usually 0x8000 bytes from the start of physical RAM) might also be different. The relocation address is coded into the uImage header by the mkimage command when the kernel is built, but it will fail if there is more than one relocation address to choose from. To put it another way, the uImage format is not compatible with multi-platform images. You can still create a uImage binary from a multi-platform build so long as you give the LOADADDR of the particular SoC you are hoping to boot this kernel on. You can find the load address by looking in mach-[your SoC]/Makefile.boot and noting the value of zreladdr-y.

In the case of a BeagleBone Black, the full command would look like this:

A kernel build generates two files in the top level directory: vmlinux and System.map. The first, vmlinux, is the kernel as an ELF binary. If you have compiled your kernel with debug enabled (CONFIG_DEBUG_INFO=y), it will contain debug symbols which can be used with debuggers like kgdb. You can also use other ELF binary tools such as size:

System.map contains the symbol table in human readable form.

Most bootloaders cannot handle ELF code directly. There is a further stage of processing which takes vmlinux and places those binaries in arch/$ARCH/boot that are suitable for the various bootloaders:

While the build is running, you will see a summary of the commands being executed:

Sometimes, when the kernel build fails, it is useful to see the actual commands being executed. To do that, add V=1 to the command line:

The next step is to build the device tree, or trees if you have a multi-platform build. The dtbs target builds device trees according to the rules in arch/$ARCH/boot/dts/Makefile using the device tree source files in that directory:

The .dtb files are generated in the same directory as the sources.

If you have configured some features to be built as modules, you can build them separately using the modules target:

The compiled modules have a .ko suffix and are generated in the same directory as the source code, meaning that they are scattered all around the kernel source tree. Finding them is a little tricky but you can use the modules_install make target to install them in the right place. The default location is /lib/modules in your development system, which is almost certainly not what you want. To install them into the staging area of your root filesystem (we will talk about root filesystems in the next chapter), provide the path using INSTALL_MOD_PATH:

Kernel modules are put into the directory /lib/modules/[kernel version], relative to the root of the filesystem.