Building LineageOS kernel trees from source

There might be times in which you’d want to build the LineageOS kernel for a device without syncing the whole repo and establishing a build environment. Maybe you want to prebuilt a stable kernel for a recovery or maybe you just want to make a custom kernel targetting Lineage.

Obtaining the tools

As of the time of writing the android kernel is built using Clang and parts of GCC. This might change in the future. I might write up a new article or update this one if that happens. Or maybe not…
Since the GCC toolchain for android is getting deprecated by Google you’ll need to get the specific one used for Lineage for your device.

First thing you’ll do is look at the branch in your kernel repository. That will indicate which toolchains you’ll need to use. Note that down for now.

Then you’ll go to the LineageOS/android repository. That contains the repo manifests used for establishing a build environment. There is a lot in there but we don’t care about most of it.
At the time of writing the build system is still provided by Google, but this might change in the future. What this means ultimately is which manifest you should look for. If Google it would be in default.xml if Lineage it will be in snippets/lineage.xml.

In there you’ll look for prebuilts/ and locate the relevant toolchains.
Currently as follows:

Clone these repositories from aosp: https://android.googlesource.com

After you’ve done that look at the refs/tags in the manifest for the appropriate remote. Checkout all the cloned repositories to that branch/tag to get them to the appropriate versions.

SUCCESS! You’re now ready to build the kernel.

Getting kernel tree

Just clone the appropriate android_kernel_ repository and cd in to it. Checkout the branch to whatever version you want to build.

Building

Just in case the repo is dirty run a clean first.

make clean && make mrproper

Make the output directory

mkdir out

Create the .config from the appropriate defconfig found in /arch/<device arch>/configs
Specify ARCH, SUBARCH and your Output directory

make O=out ARCH=arm64 SUBARCH=arm mydevice_defconfig

Build the kernel.
Usually you’d export these variables, but I’ve heard that it might cause issues, so just pass them inline.
O - The output path
ARCH - Device architecture arm arm64 x86 etc (look in /arch)
SUBARCH - same as above
CC - Android uses clang now
NM - The name mangling binary. Google prefers the one from LLVM. llvm-nm
OBJCOPY - The objcopy binary. Google prefers the one from LLVM. llvm-objcopy
LD - The linker binary. Usually not setting this is fine, but some kernels will fail compiling wth the default one, usually ld.gold. Try different linkers here if it fails. From experience the LLVM linker ld.lld works well.
PATH - Append the path to the clang bin directory and the gcc bin directories. Some kernels might need more than one gcc. For example both aarch64 and arm
LD_LIBRARY_PATH - Append the path to the clang libraries directory. lib or lib64 (Might also be needed for gcc libs)
DEFCONFIG - The device defconfig name like in the previous step.
CROSS_COMPILE - The Android cross compiler prefix for the arch (from gcc). For example aarch64-linux-android- or arm-linux-androideabi- or aarch64-linux-gnu- or arm-linux-gnueabi-
CROSS_COMPILE_ARM32 - Sometimes needed in some kernels for vDSO. Set it to the appropriate gcc prefix. arm-linux-androideabi-
CLANG_TRIPLE - Clang will normally get it’s --target from CROSS_COMPILE which will break stack protector. Set this to the appropriate gnu target. For example aarch64-linux-gnu- or arm-linux-gnueabi-

PATH make -j$(nproc) O ARCH SUBARCH CC NM OBJCOPY LD LD_LIBRARY_PATH DEFCONFIG CLANG_TRIPLE CROSS_COMPILE CROSS_COMPILE_ARM32

Cogratulations! You’ve now built a kernel

Building DTB and DTBO

Some devices use Device Tree Overlays. These have various descriptions of hardware similar to ACPI on BIOS. Before the kernels were specifically tailored to every device but with this feature many embedded devices can use the same kernel.
What this means for us is that we might want to ship custom DTB and DTBO images, and to do so we must build them.

Obtaining the tools

git clone https://android.googlesource.com/platform/system/tools/mkbootimg
git clone https://android.googlesource.com/platform/system/libufdt

Build the kernel

If you haven’t already, you’ll need to build the kernel, like described above. Go do that

Figuring out your device’s page size

There are many ways to do so. The way we’ll do it here will involve dumping your boot.img from a device or working ROM (for example stock). You can also just steal it off of a known working device tree if you want.
Dump your boot.img and then run

unpack_bootimg.py --boot_img boot.img

This command will print your device Page Size. Note that down.

Building dt.img (dtb)

This is only needed if your device has a dtb partition. If it’s appended to the kernel this is not needed.

From the kernel building example above. Run:

PATH make dtbs -j$(nproc) O ARCH SUBARCH CC NM OBJCOPY LD LD_LIBRARY_PATH DEFCONFIG CLANG_TRIPLE CROSS_COMPILE CROSS_COMPILE_ARM32

This will generate your dtb images.
To make dt.img run:

cat $(shell find kernel/out/arch/<arch>/boot/dts/** -type f -name "*.dtb" | sort) > dt.img

Building dtbo.img

From the kernel building example above. Run:

PATH make dtbs -j$(nproc) O ARCH SUBARCH CC NM OBJCOPY LD LD_LIBRARY_PATH DEFCONFIG CLANG_TRIPLE CROSS_COMPILE CROSS_COMPILE_ARM32

This will generate your dtbo images.
To make dtbo.img run:

./libufdt/utils/src/mkdtboimg.py create dtbo.img --page_size=<page_size> $(find kernel/out/arch/<arch>/boot/dts -type f -name "*.dtbo" | sort)

Further reading: