Kernel Build Notes

Here are some notes on building a kernel for embedded systems:

= help = Use "make help" to get a list of targets that are available in the kernel build system (the kernel source's native Makefile-based system)

There are many different make targets that can be used to
 * get additional help
 * perform static checks of the kernel source
 * set the configuration options for the kernel build
 * select the image format for a kernel
 * package the kernel for later installation
 * install the kernel immediately on the system where the build is being performed
 * run tests on the kernel (either the currently executing kernel or the kernel just built)

= build variables = When building for embedded boards, you are often cross-compiling from x86_64 to a different processor architecture. The kernel supports this by allowing you to specify certain build variables. These are specified either in the shell environment or on the 'make' command line.

Note that variables declared on the make command line override variables that are found in the shell environment. Also, variables declared on the make command line override variables declared inside the Makefiles themselves.

It is customary to set ARCH and CROSS_COMPILE on the make command line. KBUILD_OUTPUT can be put either in the environment or on the make command line.

architecture
When you are doing a cross-build (building the kernel for an architecture other than the one you are on), you always need to specify the ARCH and CROSS_COMPILE variables, which indicate the machine architecture.

Common architectures to cross-build are: arm, aarch64, x86, x86_64, riscv, mips

cross-compiler prefix
The CROSS_COMPILE variable should be the prefix of the toolchain you are using. For example if you are building using the GNU toolchain collection, and specifically are using tools with the executable names: arm-linux-gnueabi-gcc, arm-linux-gnueabi-as, arm-linux-gnueabi-ld, etc., then your toolchain prefix is "arm-linux-gnueabi-". In particular, notice that the cross-compile toolchain prefix string includes the dash ("-") at the end.

Here is an example make line: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zImage

In general these variables need to be set on ever make command line (that is, every time make is invoked).

setting the build output directory
If you don't specify one, the build artifacts are put into the current source directory.

You can use a different directory by setting KBUILD_OUTPUT (either in an environment variable, or on the make command line). This is particularly useful if you build using different configurations or architectures, from the same source tree.

Example: export KBUILD_OUTPUT=../test-build-bbb ; make ARCH=arm CROSS_COMPILE=... zImage

= setting the kernel config = The kernel allows customizing it configuration, providing over 13,000 configuration options which control what things are included in the kernel, and how the kernel runs when it is executed.

You can configure the kernel manually, or using a default config ("defconfig") from an existing file, or by automatically probing the system that the build is currently running on. (This can be useful for building only the modules that are in current use on your system - see 'make localmodconfig')

The following targets are for interactively, manually, setting the kernel config: config, nconfig, menuconfig, xconfig, gconfig, oldconfig

You can set the configuration for the kernel using a file using "make {defconfig-name}"

The list of available defconfigs is under arch/$ARCH/configs. It is, unfortunately, not really feasible to figure it out on your own by looking at the config contents. Usually the name is a good hint. but often in order to figure out the right defconfig for your board, you have to search online for kernel build instructions for your board.

As an example, the defconfig for a Beaglebone Black board is 'omap2plus_defconfig', so to set the configuration for building the kernel for the Beaglebone Black board, the command is: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- omap2plus_defconfig

The configuration file is saved in a file named ".config" at the root of the build output directory (which is the root of the current source directory if KBUILD_OUTPUT is not set)

= build outputs = The main purpose of the build system is to produce the files that are necessary to execute the kernel. This includes a primary binary image for the kernel itself, and various other related files.

choosing a kernel image target format
You can select what type of kernel image to build, using the 'make' target.

Common formats are 'vmlinux', "Image", and 'zImage', but see the make help for additional image formats.

In general, a "vmlinux" or "Image" file is uncompressed, and a kernel image file with a 'z' somewhere in its name is compressed. Compressed kernels include a bit of code which self-decompresses the kernel when the bootloader loads the kernel image and transfers control to it.

images and files
The kernel build system will produce a set of images that can be used to execute and debug the Linux kernel.

The set of files produced is:
 * a copy of the config file
 * the "map" file, which has the addresses of symbols within the kernel image
 * the kernel image file, which is the kernel executable image itself
 * the kernel modules, which can be loaded dynamically after a kernel is running

kernel modules
The Linux kernel modules consists of bits of code compiled into separate binary objects that can be loaded individually, after the kernel has booted. Kernel modules files have the extention ".ko" (for "kernel object"), and on a running system they are placed in directories under the filesystem /lib/modules/{kernel_release}/kernel directory.

Additional helper files used for loading, unloading and debugging modules are also located in the modules directory. These files are: and related binary versions of these files (ending in ".bin")
 * modules.alias
 * modules.builtin
 * modules.dep and modules.softdep
 * modules.devname
 * modules.order
 * modules.symbols

These helper files are used by 'modprobe', which is a tool for loading modules which automatically loads other modules that the requested module depends on.

device tree blobs (dtbs)
Many embedded architectures support running a single kernel image on multiple different boards, using a runtime configuration structure called a "device tree". This structure describes the specific hardware that is on a particular board to the kernel, and is used when the kernel boots to configure appropriate systems and drivers.

The device tree structures are compiled from source, just as the kernel executable image is, and they are placed in a separate directory. The resulting files are called device tree blobs, and have the suffix ".dtb"

= output locations and actions = You can control where the kernel build artifacts are placed, after the build, as well as how they are packaged and what is done with them (e.g. install them directly from the build system).

default output location
By default, the results of a kernel build are placed in an architecture boot directory, and some files are placed in the top of the build output directory.

For example, if you do a kernel build for the 'arm' architecture, you will find some materials at the top directory: and some in the 'arch/arm/boot' directory:
 * map file
 * vmlinux file
 * module information files
 * Image and/or zImage files
 * dts files

packaging kernel output
The kernel build system can package your kernel build artifacts in a number of ways, directly from 'make'.

For example: 'make rpm-pkg' or 'make deb-pkg'

To make a directory of files, suitable for putting into a tarfile or other archive, use: make ... dir-pkg

This latter command will make a directory called 'tar-install' in the build output directory, with sub-directories: boot, and lib/modules, where the kernel image artifacts and kernel module artifacts are stored, respectively.

installing modules
You can control where modules are installed, using make modules_install

You can use the the variable INSTALL_MOD_PATH to control where modules are installed to. If INSTALL_MOD_PATH is not specified, kernel modules are installed under the directory /lib/modules/{kernelrelease}/

installing the kernel
You can directly install a kernel image, from the kernel build system, using make install

= kernel release name = The release name of the kernel is used for naming the directory under lib/modules where modules reside, and as suffixes on the names of kernel artifacts in the boot directory.

The name is made from several parts, which are defined in the Makefile at the top of the source directory:
 * VERSION
 * PATCHLEVEL
 * SUBLEVEL
 * EXTRAVERSION

to this is appended the git commit number (if building from a git source tree). An example kernel release is: 6.4.0-rc4-g884fe9da1b7c

If you do a 'make ... -s kernelrelease, it will also include the 'git describe' patch count since the latest tag: 6.4.0-rc4-00073-g884fe9da1b7c

This is also in the file {output_dir}/include/config/kernel.release