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.

Compiling the kernel image

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

  • U-Boot: Traditionally U-Boot has required a uImage, but newer versions can load a zImage file using the bootz command
  • x86 targets: It requires a bzImage file
  • Most other bootloaders: It requires a zImage file

Here is an example of building a zImage file:

$ make -j 4 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- zImage

Tip

The -j 4 option tells make how many jobs to run in parallel, which reduces the time taken to build. A rough guide is to run as many jobs as you have CPU cores.

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:

$ make -j 4 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- LOADADDR=0x80008000 uImage

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:

$ arm-cortex_a8-linux-gnueabihf-size vmlinux
  text     data      bss        dec       hex    filename
8812564   790692   8423536   18026792   1131128   vmlinux

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:

  • Image: vmlinux converted to raw binary.
  • zImage: For the PowerPC architecture, this is just a compressed version of Image, which implies that the bootloader must do the decompression. For all other architectures, the compressed Image is piggybacked onto a stub of code that decompresses and relocates it.
  • uImage: zImage plus a 64-byte U-Boot header.

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

$ make -j 4 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf-zImage
CC     init/main.o
CHK    include/generated/compile.h
CC     init/version.o
CC     init/do_mounts.o
CC     init/do_mounts_rd.o
CC     init/do_mounts_initrd.o
LD     init/mounts.o
[...]

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:

$ make ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- V=1 zImage
[...]
arm-cortex_a8-linux-gnueabihf-gcc -Wp,-MD,init/.do_mounts_initrd.o.d  -nostdinc -isystem /home/chris/x-tools/arm-cortex_a8-linux-gnueabihf/lib/gcc/arm-cortex_a8-linux-gnueabihf/4.9.1/include -I./arch/arm/include -Iarch/arm/include/generated/uapi -Iarch/arm/include/generated  -Iinclude -I./arch/arm/include/uapi -Iarch/arm/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-dwarf2-cfi-asm -mabi=aapcs-linux -mno-thumb-interwork -mfpu=vfp -funwind-tables -marm -D__LINUX_ARM_ARCH__=7 -march=armv7-a -msoft-float -Uarm -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-but-set-variable -fomit-frame-pointer -fno-var-tracking-assignments -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -DCC_HAVE_ASM_GOTO    -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(do_mounts_initrd)"  -D"KBUILD_MODNAME=KBUILD_STR(mounts)" -c -o init/do_mounts_initrd.o init/do_mounts_initrd.c
[...]

Compiling device trees

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:

$ make ARCH=arm dtbs
...
DTC     arch/arm/boot/dts/omap2420-h4.dtb
DTC     arch/arm/boot/dts/omap2420-n800.dtb
DTC     arch/arm/boot/dts/omap2420-n810.dtb
DTC     arch/arm/boot/dts/omap2420-n810-wimax.dtb
DTC     arch/arm/boot/dts/omap2430-sdp.dtb
...

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

Compiling modules

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

$ make -j 4 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- modules

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:

$ make -j4 ARCH=arm CROSS_COMPILE=arm-cortex_a8-linux-gnueabihf- INSTALL_MOD_PATH=$HOME/rootfs modules_install

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

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.45.5