Skip to main content

Compiling the Kernel

Overview / Credits​

This article is heavily based on Hirbod Behnam's articles. Start by reading Compiling the Linux kernel and creating a bootable ISO from it. We'll be turning his articles into a bash script.

Try going through the article first and creating a working kernel.

Getting Started​

Create a new folder to put all the scripts in.

Create a file build_kernel.sh.

build_kernel.sh
# mkdir creates a directory
# The -p flag will create all parent directories.
# It also hides if the directory already exists.

mkdir -p compile_kernel
mkdir -p downloads
mkdir -p inputs

# cd changes the active directory
# This means wget will download to the downloads folder instead of the root folder
cd downloads

# Caching file downloads helps save bandwidth for the people hosting files
if [ -f "linux-7.0.11.tar.xz" ]; then
echo "Skipped downloading Linux. Using cached download"
else
wget https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.0.11.tar.xz
fi

# This unzips the archive. Google or AI will usually tell you the correct syntax for unzipping files
tar xf linux-7.0.11.tar.xz

You can run the entire bash script by opening a terminal in the directory with the script and running ./build_kernel.sh

You can copy and paste parts of the bash script into your terminal to test them line by line. This helps pinpoint problems.

This article uses kernel 7-0.11. You can find a more up to date kernel at https://cdn.kernel.org/pub/linux/kernel.

Compiling the Kernel​

In order to compile the linux kernel, you'll need to install some packages.

apt install build-essentials flex libncurses5-dev bc libelf-dev bison

There's a few options which are important configuration options when compiling the kernel.

  • CONFIG_KERNEL_GZIP=y This ensures the kernel is compressed.
  • Certificates #CONFIG_SYSTEM_TRUSTED_KEYS="" #CONFIG_SYSTEM_REVOCATION_KEYS=""

The eaisest way to get a working kernel is to use the config of your current os. Check /boot on your local system for files named config. On PopOs, one may be called config-7.0.11-76070011-generic. Copy this into the inputs folder and name it kernel_config.

We'll continue our script

build_kernel.sh

# This changes the directory back to our project root
cd ../

# We'll copy our unziped kernel code and build config into the compile_kernel folder.
# Seperating our inputs, cached downloads, and build folders makes it easy to do a "clean build"
# The compile_kernel folder can be deleted between builds.
# This will allow every build to start from a clean slate.
cp -r downloads/linux-7.0.11 compile_kernel/linux
cp inputs/kernel_config compile_kernel/linux/.config

cd compile_kernel/linux

make defconfig

We're now ready to build the kernel.

Running make will compile the kernel.

You can use also use multiple cores to significantly speed up the build make -j $(nproc).

Using make provides more descriptive errors. Sometimes errors will appear immediately after running make, but won't appear until make -j $(nproc) finishes compiling.

Compile the kernel before continuing. Check that the kernel file exists: arch/x86/boot/bzImage.

We'll continue adding to build_kernel.sh

build_kernel.sh
make -j $(nproc)

# Challenge:
# Create an if block checking if arch/x86/boot/bzImage exists. Output an error message if it doesn't.
# You can reference the if code from earlier in the article

# Because the kernel takes so long to build, we'll save it to the inputs folder
# instead of the compile_kernel folder which gets deleted after every run.
cp arch/x86/boot/bzImage ../../inputs/bzImage

BusyBox​

Busybox provides utilities and commands for the linux system to use. For example, it allows for moving files, compressing directories, and killing processes.

BusyBox doesn't provide all "Posix commands" meaning programs and scripts which work on Ubuntu may not work.

CoreUtils is commonly used, and Ubuntu is replacing this with UUtils which aims to do everything CoreUtils.

Now we'll download and install BusyBox. Create a new script build_busybox.sh

build_busybox.sh
cd downloads

if [ -f "busybox-1.38.0.tar.bz2" ]; then
echo "Skipped downloading BusyBox. Using cached download"
else
wget https://busybox.net/downloads/busybox-1.38.0.tar.bz2
fi

tar xf busybox-1.38.0.tar.bz2
cp -R busybox-1.38.0 ../compile_busybox

cd ../compile_busybox


make defconfig

sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
sed -i 's/CONFIG_TC=y/CONFIG_TC=n/' .config

make -j $(nproc)

#Check if file exists
#file busybox

make install

cd _install
mkdir -p dev proc sys

Create a new file called init with no extension inside the inputs folder. This script comes from the article linked above.

inputs/init
#!/bin/sh
mount -t devtmpfs none /dev
mount -t proc none /proc
mount -t sysfs none /sys
echo "Welcome to my Linux!"
exec /bin/sh

Add code to build_busybox.sh to copy in the init script. Then we'll zip everything in busybox's _install folder.

build_busybox.sh
cp ../../inputs/init init
chmod +x init

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../../inputs/initramfs.cpio.gz

cd ../../../

Testing the Kernel​

To test the kernel, cd into the inputs folder and this command.

cd inputs
qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz