Skip to content

Repository files navigation

Caution

By using this guide, you accept all risks - including potential device bricking, failed boots, or other issues. We take no responsibility for any damage.

Questions will only be considered if you've read the full documentation and done your own research first.

A Beginner-Friendly Guide to Compile Your First Android Kernel!

Android Linux License: GPL v3 Telegram

What You'll Learn:

  • Understanding the kernel root & choosing the right compilers for compilation
  • Compiling a kernel by hand, and then the easy way with a build script
  • Customizing the kernel and applying kernel patches.
  • Remove Samsung's anti-root protections.
  • Creating a signed boot image from the compiled kernel

Requirements:

  • A working 🧠
  • Patience
  • A x86_64 (AMD64) Linux-based PC/Server (Debian-based recommended)
  • Basic knowledge of Linux commands and Bash scripting
  • Basic knowledge of version control (Git)
    • This is good practice when building a kernel. Imagine you edit some files and realize you've messed up the source - this one single command git reset --hard can help you revert all the uncommitted changes you made. How cool is that :)

    • Go learn some Git from here before you start learning kernel compilation!

πŸ›  Install required dependencies for compiling kernels

Tip

For the most reliable and hassle-free experience, we strongly recommend using our pre-configured Docker container which provides a stable, tested environment for kernel compilation that works on any OS. Download it from the releases page and follow the included instructions.

Want to build the image yourself instead of downloading it? See docker/.

Expand to view how the Docker container looks like

Screenshot of the kernel-builder Docker container running on Fedora

Screenshot of the Ubuntu-based Docker container running on Fedora. Click to view in full quality.

But, if you don't want to use the Docker container, here are the commands to install the dependencies for Ubuntu/Fedora:

🟧 Ubuntu/Debian-based distributions (Ubuntu, Linux Mint, Debian, etc.)
sudo apt update && sudo apt install -y \
	build-essential bc bison flex patch pkg-config git curl tar xz-utils zip unzip \
	cpio rsync kmod perl python3 python-is-python3 libssl-dev libelf-dev pahole \
	libncurses-dev zlib1g-dev libyaml-dev lz4 zstd device-tree-compiler adb fastboot

[!NOTE] Ubuntu 24.04+ no longer ships libtinfo5, which the old Snapdragon LLVM toolchain (Linux 5.4 / qGKI) needs. Don't hunt for the .deb, the build scripts detect this and point that toolchain at your system libtinfo.so.6 instead.

🟦 Fedora/Red Hat-based distributions (Fedora, CentOS, RHEL, etc.)
sudo dnf install -y --skip-unavailable \
	make gcc gcc-c++ bc bison flex patch pkgconf git curl tar xz zip unzip \
	cpio rsync kmod perl python3 openssl openssl-devel openssl-devel-engine \
	elfutils-libelf-devel dwarves ncurses-devel ncurses-compat-libs \
	zlib-devel libyaml-devel lz4 zstd dtc android-tools

Note

If you use a build script from this repo, you can skip this step. The script checks these packages every time it runs and installs only the ones you are missing.

Quick links

  1. πŸ“ Downloading the kernel source
  2. 🧠 Understanding the kernel root
  3. 🧠 Understanding non-GKI and GKI kernels
  4. 🧰 Choosing the right compiler
  5. πŸ‘€ Preparing for the compilation
  6. πŸ”§ Customizing the kernel, temporary method
  7. πŸ”§ Customizing the kernel, permanent method
  8. πŸ”“ Nuking Samsung's anti-root protections
  9. 🟒 Additional patches
  10. βœ… Compiling the kernel
  11. πŸŸ₯ Fixing known compiling issues
  12. 🟑 Building a signed boot image

Note

If you are not a beginner and want to build a GKI 2.0 kernel from the official Google sources, jump to the gki-2.0 branch.

Some of those sources (android14-5.15, android14-6.1, android15-6.6) need Bazel rather than plain make. See Building GKI 2.0 kernels with Bazel.

Credit to @TheWildJames for the awesome tutorial!


πŸ“ 01. Downloading the kernel source

  • ⚠️ If your device is Samsung,

01. Download the kernel source from Samsung Opensource

Screenshot of the Samsung Opensource site with a kernel source search result

02. Extract the Kernel.tar.gz

tar -xvf Kernel.tar.gz && rm Kernel.tar.gz

Screenshot of the terminal extracting Kernel.tar.gz

Note: It's good practice to fix the permissions on the extracted kernel tree before you start working on it - take ownership of the directory and clear the read-only attributes on the files and folders. Otherwise you'll hit problems editing files and upstreaming the kernel later.

Run this to fix it:

sudo chown -R "$(id -un):$(id -gn)" "/path/to/extracted/kernel/" && \
  chmod -R u+rwX "/path/to/extracted/kernel/"

Before: Screenshot of the kernel tree before fixing permissions, files shown as read-only

After: Screenshot of the same kernel tree after taking ownership


  • ⚠️ For other devices, You can find them by your OEM's sites or from your OEM's official GitHub repos:

    Screenshot of an OEM's official GitHub organisation listing kernel sources

🧠 02. Understanding the kernel root

You downloaded a source code archive, but which folder inside it is the actual kernel? That folder is called the kernel root, and every command in this guide runs from there.

Screenshot of a kernel root, with the expected folders highlighted in blue

  • As you can see in the above screenshot, it's the Linux kernel source code.

  • It must have those folders, highlighted in blue in the terminal.

  • In traditional GKI kernels, the kernel root is located in a folder named "common".

  • In GKI Samsung Qualcomm kernel sources, you should use the common kernel instead of msm-kernel for compilation.

  • In some GKI Samsung MediaTek kernel sources, the kernel root is named kernel-VERSION.PATCHLEVEL.

    • e.g., kernel-5.15

Tip

A quick way to confirm you are in the right place: run make kernelversion there. If it prints a version number like 5.15.123, that's your kernel root.

🧠 03. Understanding non-GKI and GKI kernels

01. GKI project introduction

  • Generic Kernel Image, or GKI, is an Android's project that aims for reducing kernel fragmentation, (and also improving Android stability), by unifying kernel core and moving SoC and Board support out of the core kernel into loadable vendor modules.

02. pre-GKI/non-GKI and GKI linux version table

Pre-GKI GKI 1.0 GKI 2.0
3.10 5.4 5.10
3.18 5.15
4.4 6.1
4.9 6.6
4.14
4.19

Explanation:

  1. pre-GKI or non-GKI:

    • The oldest Android kernel branch, likely starts from Linux version 2.x.
    • These kernels are device-specific because its often heavily modified to accommodate SoCs and OEMs needs.
    • Starting to get deprecated in ACK, since linux-4.19.y branch already reaching EoL (End of Life) state, with last Linux 4.19.325
  2. GKI 1.0:

    • Android's first generation of the Generic Kernel Image, starting with kernel version 5.4.
    • This first generation of GKI only have android11-5.4 and android12-5.4 branch and Google announced that GKI 1.0 is deprecated.
    • The first generation of GKI is not yet matured as second generation of GKI, as its failed to reach GKI project goals.
    • These kernels are considered as device-specific, but more commonized, depends on how OEMs and SoCs Manufacturer treat them.
    • SoC Manufacturers often modify GKI 1.0 kernel to add their SoC features. From this modifications, the term Mediatek GKI (mGKI) and Qualcomm GKI (qGKI) exist.
  3. GKI 2.0:

    • Android's second generation of the Generic Kernel Image, starting with kernel version 5.10.
    • In this second generation, GKI project starting to get matured properly.
    • This kernel is considered as "universal", since you can boot a GKI kernels that builded with Google's GKI kernel source on some devices, if correct and match.

Notes:

  • LTS = Long-Term Support: These kernels are stable, well-maintained, and receive long-term updates.

  • GKI = Generic Kernel Image: A unified kernel framework introduced by Google to standardize the kernel across Android devices.

  • SoC = System on Chip

  • ACK = Android Common Kernel: An Android's linux LTS kernel branch, modified to accommodate Android needs.

  • OEMs like Samsung may still modify GKI 2.0 kernels to accommodate their needs, and can cause some issues like broken SD Card and broken Audio.

    • So, use their GKI kernel source instead if possible.
  • For 4.19 kernels, they are predominantly non-GKI implementations, as true GKI was not officially introduced until kernel 5.4 with Android 11.

    • OEMs typically use heavily customized, device-specific implementations based on the Android Common Kernel for 4.19. You can refer to the Android Common Kernel repository if you are interested.
    • For your information, there was experimental GKI development with 4.19 (android-4.19-gki-dev branch), but this was not widely deployed. Official GKI implementation began with kernel 5.4.
    • Examples:
      1. Most Samsung devices with kernel 4.19 use non-GKI implementations with OEM-specific modifications.
      2. True GKI adoption became standard with newer devices shipping Android 11+ with kernel 5.4 or higher.

🧰 04. Choosing the right compiler

Your phone's kernel was built with a specific compiler. If you build it with something too new or too old, the build fails, or worse, it builds fine and then refuses to boot. So this step comes before anything else.

01. Check your kernel version

Run this inside your kernel root:

make kernelversion

Screenshot of make kernelversion printing the kernel version

You can also read it straight from the Makefile at the top of the kernel root:

Screenshot of the top of a kernel Makefile showing VERSION, PATCHLEVEL and SUBLEVEL Kernel version = VERSION.PATCHLEVEL.SUBLEVEL

Only the first two numbers matter here. 4.14.113 is a 4.14 kernel, 5.15.123 is a 5.15 kernel.

02. Pick the compiler for that version

Here is the short version of the table. Full details and download links are here, based on my own experience.

Kernel version Build script Toolchain it uses
4.9 build_4.9.sh Proton Clang 12 + Linaro GCC 7.5
4.14 (OEM/stock source) build_4.14.sh clang-r383902b + ARM GNU
4.14 (AOSP/LineageOS source) build_4.14_aosp.sh Neutron Clang (any recent Clang works)
4.19 build_4.19.sh clang-r353983c + ARM GNU
5.4 (Qualcomm, aka qGKI) build_5.4.sh or build_qGKI.sh Snapdragon LLVM + ARM GNU
5.10 build_5.10.sh clang-r416183b
5.15 build_5.15.sh clang-r450784e
6.1 and newer build_6.1.sh clang-r510928

build_5.4.sh and build_qGKI.sh are the same script under two names, because qGKI kernels are 5.4 kernels.

Note

You don't have to download any of these toolchains by hand. If you use a build script, it downloads the right one into ~/toolchains on the first run. This table is here so you know what your script is doing.

03. Clang only, or Clang plus a GCC cross compiler?

This trips up a lot of beginners, so here it is in plain words.

  • Linux 4.9 up to 5.4, stock or OEM source: you need both a Clang and a GCC cross compiler. The kernel's build system of that era still calls GCC tools like aarch64-linux-gnu-ld and aarch64-linux-gnu-as to assemble and link, even when Clang compiles the C code.

  • Linux 4.9 up to 5.4, AOSP or LineageOS source: usually Clang only. This is the part people miss. AOSP and LineageOS backport the newer LLVM support into their old trees, so a 4.14 LineageOS kernel normally builds with LLVM=1 even though the 4.14 stock kernel for the same phone does not. That is why there are two scripts for 4.14.

  • Linux 5.10 and newer: you only need Clang. Passing LLVM=1 tells the kernel to use the LLVM versions of every tool (clang, ld.lld, llvm-ar, llvm-nm, llvm-objcopy, llvm-strip), so no GCC cross compiler is downloaded or used at all.

LLVM_IAS=1 goes with it. It tells Clang to assemble the code itself instead of calling an external assembler. On 5.15 and newer this is already the default, so you will see it passed mostly for 5.10.

If you look inside the newer build scripts, this is the whole compiler setup:

BUILD_OPTIONS=(
    -j"$(nproc)"
    ARCH=arm64
    LLVM=1
    LLVM_IAS=1
    HOSTCC=gcc
    HOSTCXX=g++
)

No CROSS_COMPILE and no GCC anywhere. Just Clang :)

πŸ‘€ 05. Preparing for the compilation

  • There are 2 ways to compile the kernel.
  1. Without a build script.
  2. With a build script.

If you are a beginner, I recommend trying to build the kernel without a build script first. Once you understand the logic, you can then use a build script to make your life easier :)


🟠 Method 1: Without a build script.

This method is here so you understand what a build script does for you. Everything below is done by hand.

01. Download and extract your compiler.

You already know which compiler you need from step 04. Download it and extract it into its own folder, like this:

Screenshot of an extracted clang folder Extracted clang

Screenshot of an extracted GCC cross compiler folder Extracted cross compiler (only needed for stock 4.9 up to 5.4 trees)

In my case the kernel is 4.14.113, so I use clang-r383902b and arm-gnu-toolchain-14.2.rel1.


02. Exporting the compiler locations to the PATH

  • Even though we downloaded the right compilers, our system (Host OS) will not automatically know which compiler to use for building our kernel.

  • By default, it will use the system's compilers, which might be incompatible with older kernels.

    • In such a case, the build will fail instantly.
  • So, our task is to wire up the downloaded compilers to our system's PATH. We must tell the system: "use the clang binary from here, not your own clang!"


πŸ’‘ What is PATH?

PATH is an environment variable in Linux/Unix that stores a list of directories.

When you type a command (like clang or gcc), the system looks through the directories in PATH from left to right to find the first matching executable.

By adding your downloaded compiler's folder to the begining of the PATH, you make sure the build system picks your compiler instead of the system default.


  • To check what your PATH variable looks like, you can type echo $PATH in the terminal:

    Screenshot of echo $PATH printing the current PATH

    • Our goal is to add our compilers' locations to the left side of /usr/local/sbin :)
  • In the extracted compiler folders, the binary files (executables) are usually located inside the bin folder, like this:

    Screenshot of the bin folder inside an extracted toolchain

  • Copy the full path to that bin folder and export those locations to the PATH like this:

    export PATH="/path/to/first/compiler/bin:/path/to/second/compiler/bin:$PATH"
  • In my case, it looked like this:

    export PATH="/home/kernel-builder/toolchains/clang-r383902b/bin:/home/kernel-builder/toolchains/gcc/arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-linux-gnu/bin:$PATH"

As you can see, we have successfully exported the toolchains to our PATH:

Screenshot of the toolchains exported to PATH

For confirmation, type clang -v in the terminal to verify that it is actually wired up!

Screenshot of clang -v confirming the right compiler is in use We did it!


03. Compiling the kernel with make

  • Keep in mind that the PATH variable we exported in Step 02 is only valid in the currently opened terminal.

    So, don't close it - use that terminal window to navigate the kernel source and run commands for further compilation.

  • Now, using that terminal window, navigate to your root of the kernel source like this: cd /path/to/kernel-root

    Screenshot of the terminal changed into the kernel root


πŸ’‘ Better to Know: A defconfig (default configuration) is like a preset settings file for the kernel.

  • It tells the build system which features to enable or disable.
  • Common defconfig locations are arch/arm64/configs or arch/arm64/configs/vendor.

  • In my case, my defconfig is located at arch/arm64/configs, and its name is exynos9820-beyondxks_defconfig.

    • Also, I have multiple defconfigs made for my specific purposes, named: common.config, ksu.config, and nethunter.config.
    • You can also create your own customized defconfigs for specific changes (more on that later)!
  • Now, we need to tell our compilers to "use these defconfigs to build the kernel"!

  • To do that, simply run the following command:

make \
  ARCH=arm64 \
  CC=clang \
  CROSS_COMPILE=aarch64-none-linux-gnu- \
  CLANG_TRIPLE=aarch64-none-linux-gnu- \
  your_defconfig your_second_defconfig your_third_defconfig

πŸ’‘ Explanation:

  1. ARCH=arm64 β†’ Specifies the architecture of the kernel we are building.

    • In our case, it is 64-bit ARM.
  2. CC=clang β†’ Tells make to use the clang compiler.

    • Don't change this value. Keep it as it is!
  3. CROSS_COMPILE=aarch64-none-linux-gnu- β†’ Prefix for the cross-compiler binaries (e.g., aarch64-none-linux-gnu-gcc).

    • You can get this value by opening your GCC's bin folder. All the binaries have the same prefix!

    Screenshot of the GCC bin folder, with the shared binary prefix highlighted See the highlighted part. aarch64-none-linux-gnu- is the common prefix for all the binaries, and it is the value for the CROSS_COMPILE variable.

  4. CLANG_TRIPLE=aarch64-linux-gnu- β†’ Tells Clang exactly which target architecture, OS, and ABI to compile for.

    • Ensures the kernel build system can enable features and flags specific to ARM64 Linux.
    • This does not require a literal binary named aarch64-linux-gnu- in your path. Clang uses it internally as a target specification.
    • You can also use aarch64-none-linux-gnu- as the triple; the vendor field (none) is usually ignored by Clang.
  5. your_defconfig ... β†’ These are the configuration files (defconfigs) that define which kernel features, drivers, and options to include in the build.

This is the barebone make command for a stock 4.9 up to 5.4 kernel. Don't remove any part of it!

Important

Building a 5.10 or newer kernel, or an AOSP/LineageOS tree of any version? Drop CROSS_COMPILE and CLANG_TRIPLE, and use LLVM=1 LLVM_IAS=1 instead:

make ARCH=arm64 LLVM=1 LLVM_IAS=1 your_defconfig

You don't need the GCC cross compiler for those kernels at all. See step 04 for the reason.


  • Now, when you run that above command, the build system will read all of your defconfig files and merge them into a single file called .config !

    Screenshot of the kernel root before running the defconfig command Screenshot before running the command

    Screenshot of the same folder after the command, now with an out directory Screenshot after running the command

This will write the final configuration to a hidden file named .config, which will be used by the build system to compile the kernel:

Screenshot of the generated .config file


  • Before compiling the kernel, if you want to edit the contents of the .config in a GUI way, you can use the menuconfig tool.

  • To launch menuconfig, type the same beginning of the command you used to create the .config (i.e., the CC and CROSS_COMPILE parts), but at the end, instead of defconfig names, use menuconfig like this:

make \
  ARCH=arm64 \
  CC=clang \
  CROSS_COMPILE=aarch64-none-linux-gnu- \
  CLANG_TRIPLE=aarch64-none-linux-gnu- \
  menuconfig

Screenshot of menuconfig opened by hand with make menuconfig It will open something like this. Feel free to edit it according to your needs.

Use the arrow keys to navigate through menuconfig. Once you are done editing, exit menuconfig to proceed with building the kernel.

Note: The customization part is not discussed here; it is covered in Method 2. This is just the barebones of "Compiling the kernel."


  • Now, we have successfully created the final configuration file (.config) and, if needed, customized it using menuconfig.

  • The only thing left to do is compile the kernel!

  • To compile, run the same command as before with the same beginning (the ARCH, CC, and CROSS_COMPILE parts), but this time do not specify any defconfig or menuconfig at the end. Like this:

make \
  ARCH=arm64 \
  CC=clang \
  CROSS_COMPILE=aarch64-none-linux-gnu- \
  CLANG_TRIPLE=aarch64-none-linux-gnu-

πŸ’‘ What this does:

This command tells the build system to start compiling the kernel immediately using the .config you just created. All the settings and options from .config will now guide the build process.


Once you run the above command, the build system will start compiling the kernel in the same kernel root directory:

Screenshot of the kernel compiling in the terminal

When it finishes, your kernel image is at arch/arm64/boot/Image.

Barebone training is enough

Let's jump into the easiest and laziest method you can do xD We'll explore the compilation more deeply in Method 02!


🟠 Method 2: With a build script.

A build script does everything from Method 1 for you: it installs missing packages, downloads the right compiler, exports the PATH, runs make with the correct options, and copies the finished kernel somewhere easy to find.

01. Download the script and put it in your kernel root.

Go to build_scripts and pick the script that matches your kernel version, using the table in step 04 or the one on that page. Download it and place it inside your kernel root, next to the Makefile:

Screenshot of the build script sitting in the kernel root next to the Makefile

Note

The script must sit in the kernel root. If you run it from anywhere else it stops immediately with Run this from the kernel source root. instead of failing halfway through a build.


02. Edit the SETTINGS block.

Open the script in a text editor. The top of every script looks like this, and it is the only part you normally need to touch:

# ---------------------------------------------------------------------------
#  SETTINGS -- the only part you normally need to touch
# ---------------------------------------------------------------------------
DEFCONFIG="gki_defconfig"      # name from arch/arm64/configs (also: vendor/foo_defconfig)
EXTRA_CONFIGS=()               # fragments merged on top, e.g. (custom.config)
KERNEL_IMAGE="Image"           # Image | Image.gz | Image.gz-dtb  (MediaTek needs Image.gz)
USE_OUT_DIR=1                  # 0 = build in-tree; most Samsung Exynos trees need 0
MENUCONFIG=1                   # 0 = skip the menuconfig GUI
export KBUILD_BUILD_USER="@ravindu644"

# Some OEM trees need extra variables -- check README_Kernel.txt or build_kernel.sh:
# export TARGET_SOC=s5e9925 PLATFORM_VERSION=12 ANDROID_MAJOR_VERSION=s
# ---------------------------------------------------------------------------

Here is what each setting does:

DEFCONFIG is the only one you must change.

  • Set it to your device's defconfig, which lives in arch/arm64/configs.

  • On GKI 2.0 kernels it is normally gki_defconfig.

  • Not sure which one is yours? Look inside arch/arm64/configs and arch/arm64/configs/vendor.

  • If your defconfig is inside the vendor folder, include that folder name too:

    DEFCONFIG="vendor/name_of_the_defconfig"

    Screenshot of the SETTINGS block with DEFCONFIG set to a vendor defconfig

EXTRA_CONFIGS is a list of extra config fragments merged on top of your defconfig.

  • Leave it as () for now. You will use it in the Permanent Method section:

    EXTRA_CONFIGS=(custom.config)
  • You can list more than one: EXTRA_CONFIGS=(custom.config ksu.config)

KERNEL_IMAGE is which kernel image to build.

  • Image is the raw one and works for most devices.
  • πŸ”΄ MediaTek devices usually cannot boot a raw Image, so set it to Image.gz there.
  • Some older trees want Image.gz-dtb.

USE_OUT_DIR decides where the build happens.

  • 1 builds into a separate out folder, which keeps your source clean.
  • ❗ Samsung Exynos trees usually cannot build into a separate folder, so set it to 0 there. The build then happens inside the kernel root itself.

MENUCONFIG decides whether the config GUI opens before the build.

  • 1 opens menuconfig every time, which is what the Temporary Method section uses.
  • Set it to 0 once you are done experimenting and just want the build to run start to finish.

KBUILD_BUILD_USER is the "built by" name baked into the kernel. Put your own name there :)


Important

If your device is Samsung, it usually uses some device-specific variables in "some" kernels.

  • As an example, in the Galaxy S23 FE kernel source code, we can see they used variables called TARGET_SOC=s5e9925, PLATFORM_VERSION=12, and ANDROID_MAJOR_VERSION=s

  • If we didn't export those variables correctly, the kernel failed to build in my case.

  • Don't worry, they usually mention these required variables in their README_Kernel.txt or their own build_kernel.sh

    Screenshot of a README_Kernel.txt listing the OEM build variables

To add them, uncomment the export line at the bottom of the SETTINGS block and put your own values there:

export TARGET_SOC=s5e9925 PLATFORM_VERSION=12 ANDROID_MAJOR_VERSION=s

Note: Just don't overthink it, even if they use values like 12 and S for Platform and Android versions, even if you have a higher Android version.


03. Edit the Makefile.

  • If you find these variables: REAL_CC or CFP_CC in your "Makefile", remove them from the "Makefile", then Search for "wrapper" in your Makefile. If there's a line related to a Python file, remove that entire line/function as well.

    • Example patch of removing the wrapper: click here

04. Grant executable permissions to the script

chmod +x build_xxxx.sh

05. Run the build script

./build_xxxx.sh

Screenshot of the build script starting in the terminal

What happens when you run it

  1. It checks your packages. It asks your package manager which of the required packages are missing and installs only those. Nothing is installed if you already have everything, so this is quick on every run after the first.

  2. It downloads your toolchain into ~/toolchains, but only if it isn't there yet. This is the slow part of the first run, and it only happens once.

    Screenshot of the script downloading a toolchain into the toolchains folder

  3. It builds your .config from the DEFCONFIG you set, plus anything in EXTRA_CONFIGS.

    Screenshot of the kconfig stage running, with harmless warnings scrolling past

  4. menuconfig opens so you can make changes by hand, unless you set MENUCONFIG=0. Close it and the compile starts.

    Screenshot of menuconfig opened by the build script

Tip

If the toolchain download fails or you interrupt it, just run the script again. A failed download deletes its own folder, so the next run starts clean. You do not need to remove ~/toolchains by hand.

  • Additional notes:
    • You can completely ignore anything displayed as warning:
      • Eg: warning: ignoring unsupported character '

πŸ”§ 06. Customizing the kernel, temporary method

  • Once the menuconfig appears, you can navigate through it and customize the Kernel in a graphical way as needed.

  • As an example, we can customize the Kernel name, enable new drivers, enable new file systems, disable security features, and more :)

You can navigate the menuconfig using the arrow keys (← β†’ ↑ ↓) on your keyboard, and press y to enable, n to disable or m to enable as a module <M>.

1. Changing the Kernel name.

  • I guess no explanation is needed for this:

    Screenshot of the local version option in menuconfig
  • Located in: General setup ---> Local version - append to kernel release

Animation of changing the kernel name in menuconfig

2. Enabling BTRFS support.

  • Btrfs is a modern Linux filesystem with copy-on-write, snapshots, and built-in RAID, ideal for reliability and scalability.

  • Located in: File systems ---> < > Btrfs filesystem support

Animation of enabling Btrfs support in menuconfig

3. Enabling more CPU Governors

  • CPU governors control how the processor adjusts it's speed.
  • You can choose between performance-focused governors (like "performance" for max speed) or battery-saving ones (like "powersave").
  • Please note that this may impact your SoC's lifespan if the device overheats while handling performance-intensive tasks.

Enabling more CPU Governors:

  • Located in: CPU Power Management ---> CPU Frequency scaling

Animation of enabling more CPU governors in menuconfig

Changing the Default CPU Governor:

  • Located in: CPU Power Management ---> CPU Frequency scaling ---> Default CPUFreq governor (performance) --->

Animation of changing the default CPU governor in menuconfig

4. Enabling more IO Schedulers

  • IO schedulers control how your system handles reading and writing data to storage.
  • Different schedulers can make your system faster or help it run smoother, depending on what you're doing (like gaming, browsing, or saving battery).
  • Located in: IO Schedulers --->

Screenshot of the IO Schedulers menu in menuconfig

The problem with menuconfig

You have to do this every time you run the build script.

  • All the changes you've made using menuconfig are saved in a hidden file called .config. It sits inside the out folder, or inside the kernel root if you set USE_OUT_DIR=0.

    Screenshot of the generated .config file inside the out folder

  • and it resets every time you run the build script.

    Screenshot of the .config being reset on the next build

  • So, we need a permanent method to save our changes, right?

πŸ”§ 07. Customizing the kernel, permanent method

  • In this method, we are going to create a separate custom.config to store our changes and tell the build script to use it.

  • After that, when we run the build script, it will first use your OEM defconfig to generate the .config file, then merge the changes from our custom.config into .config again.

01. Create the file

Create custom.config inside arch/arm64/configs, next to your defconfig.

02. Tell the script about it

Add it to EXTRA_CONFIGS in the SETTINGS block:

DEFCONFIG="your_defconfig"
EXTRA_CONFIGS=(custom.config)

That's it. Your defconfig is applied first, then custom.config is merged on top, so anything you put in custom.config wins.

Note

Doing this by hand instead? The same thing works with plain make, just list the fragment after your defconfig: make ARCH=arm64 LLVM=1 your_defconfig custom.config


πŸ€“ How to add changes to our custom.config ?

  • First, We have to find the exact kernel configuration option you want to enable or disable.

  • Example kernel configuration option: CONFIG_XXXX=y

    • CONFIG_XXXX: The name of the kernel option or feature ( Must begin with CONFIG_ )
    • =y: This means "yes" -> the option is enabled and will be included in the kernel.
    • =n: This means "no" -> the option is disabled.
  • You can find the name of the kernel configuration option this way:

    • Run the build script and wait until menuconfig appears.

    • Navigate to the option/feature you want to enable.

    • Press shift + ? on your keyboard, and an explanation about the option/feature will appear.

    • You'll see the name of the kernel configuration option in the top-left corner of the menuconfig.

      Screenshot of menuconfig help showing a config option name in the top-left corner

    • Copy that name and add it to your custom.config with =y or =n to enable or disable it.

      Screenshot of a custom.config file with config options added to it

πŸ”“ 08. Nuking Samsung's anti-root protections

This one has its own page: how to remove Samsung's RKP.

🟒 09. Additional patches

These are optional. Apply the ones you need with patch -p1 < filename.patch from your kernel root, or open the patch in an editor and make the changes by hand, which I recommend for understanding what you are doing.

01. To fix broken system functions like Wi-Fi, touch, sound etc.

Caution

This one is a last effort, not a default. The CRC check it disables is there for a real reason, and turning it off can get you memory corruption or a kernel panic instead of a clean refusal to boot. Only reach for it when the module you need is closed source (proprietary vendor drivers) and you have no way to rebuild it. If you can rebuild the module against your kernel, do that instead.

  • On some devices, compiling a custom kernel can break system-level functionalities like Wi-Fi, touch, sound, and even cause the system to not boot.

  • The reason behind this is that the device can't load the external kernel modules (*.ko), due to linux's prebuilt security feature (symversioning, signature) that prevent malicious kernel module to load.

  • To fix this issue, use this patch to force the kernel to load those modules.

Read this before you use it: what the CRC check is actually protecting

Imagine there are two structs, one of which is used by vendor modules:

struct a {
    int varA;
    double varB;
};

struct b {
    int varX;  // 32-bit
    long varY; // 64-bit
    long varZ; // 64-bit
};

Let's say struct b is used by vendor modules. The vendor modules expect the variables to be at specific memory offsets. Conceptually, it looks like this:

&b        = varX
&b + 0x20 = varY
&b + 0x60 = varZ

A CRC (Cyclic Redundancy Check) functions similarly to the hashing algorithms we use to verify file integrity. In this context, the CRC basically encodes the size, order, and data types of the variables within a struct.

Now, let's say I enable an option in the defconfig that adds a new variable, varC, to struct b between varX and varY:

struct b {
    int varX;  // 32-bit
    int varC;  // 32-bit
    long varY; // 64-bit
    long varZ; // 64-bit
};

If the vendor modules are not recompiled against this new change, they will still use the memory offsets of the old struct. The module will basically expect varY to be located at the old offset (0x20). However, due to the addition of varC, varY has now shifted to a new offset (e.g. 0x40).

If CRC checks weren't present, the modules would read from or write to the wrong memory addresses. This can have dangerous consequences, such as memory corruption or kernel panics. To prevent this, the kernel checks the CRC of the struct. A mismatch implies that the modules were not built according to the latest changes, meaning the ABI is broken or the KMI is unstable.

When this mismatch occurs, the device refuses to load the modules. Since the Generic Kernel Image (GKI) project aims to reduce fragmentation by building core code like display and GPU drivers as modules, the failure to load these critical modules results in a bootloop.

Now, what if a struct requires changes to fix critical bugs in the kernel? Wouldn't that also break the ABI?

Yes, definitely. To prevent this, Google uses ABI padding. These are essentially dummy variables (reserved space) placed inside the struct. When a fix is needed, these dummy variables are replaced with the new variables. Because we are simply "replacing" a placeholder rather than changing the relative positions of the surrounding variables, the structural layout remains unchanged. As a result, the CRC, and therefore the ABI, remains perfectly stable :)


02. Fix: There's an internal problem with your device. issue.

Screenshot of the Android System dialog saying there is an internal problem with your device

The reason:

Userspace reads /proc/config.gz and spits out an error message after boot
finishes when it doesn't like the kernel's configuration. In order to
preserve our freedom to customize the kernel however we'd like, show
userspace the stock defconfig so that it never complains about our
kernel configuration.
  • To fix this issue, make a copy of your OEM's Defconfig and rename it to stock_defconfig.

    Screenshot of the OEM defconfig copied and renamed to stock_defconfig

  • Then, use the patch below to fool Android into thinking that the defconfig was not changed:


03. Booting with SELinux set to permissive

  • Handy while you are debugging, because a lot of "it boots but nothing works" problems are just SELinux denials.

  • Two patches are involved. The first one adds the toggle to the kernel, the second one turns it on in your defconfig:

  • Don't ship a permissive kernel to normal users. It removes a real security layer of Android.


04. Wiring up KernelSU hooks

  • If you want to add KernelSU support to a kernel that doesn't have the required hooks, this patch adds them manually.


05. Removing the -dirty string from the kernel version

  • When you edit the source without committing, the kernel appends -dirty to its version string. This patch removes it and appends your own localversion instead.

βœ… 10. Compiling the kernel

  • Once you've customized the kernel as you want, simply exit menuconfig.
  • After exiting, the kernel will start compiling!

Animation of exiting menuconfig and the compile starting

πŸ’‘ If everything goes smoothly like this,

Screenshot of a finished build in the terminal

you'll find the built kernel Image inside the build folder in your kernel root!

Screenshot of the build folder containing the compiled Image

The build script copies it there for you at the end. The original also stays where make put it:

  • out/arch/arm64/boot/ when USE_OUT_DIR=1
  • arch/arm64/boot/ when USE_OUT_DIR=0, or when you built by hand with Method 1

Tip

Did your build produce kernel modules (.ko files) as well? Those need to go into the stock vendor_boot.img or vendor_dlkm.img, which is a separate job. See how to install custom kernel modules.

πŸŸ₯ 11. Fixing known compiling issues

  • If you ever encounter any errors during your kernel compilation, jump to fixes and see if your specific issue is mentioned there.

Click here to learn about known issues and their fixes

🟑 12. Building a signed boot image

  • On Android devices, the kernel image is usually located inside the boot partition.

    Diagram of an Android partition layout, with the boot partition highlighted

  • So, all we have to do is get the boot image from the stock ROM, unpack it, replace its kernel with our "built" one, repack it, flash it, and enjoy :)

For the unpacking and repacking process, we are going to use magiskboot, Magisk's built-in boot image unpacker and repacker!

01. Downloading and extracting the latest Magisk APK

02. Getting magiskboot from the extracted folder & Adding it to the system PATH

  • The magiskboot binary will be located inside the extracted_magisk_apk/lib/<arch> folder with the filename libmagiskboot.so :

    Screenshot of libmagiskboot.so inside the extracted APK's lib folder

Rename it to magiskboot and install it to your system PATH with this:

Screenshot of magiskboot being installed into the system PATH

Quick commands:

# Renaming libmagiskboot.so to magiskboot
mv libmagiskboot.so magiskboot

# Giving magiskboot executable permissions
chmod +x magiskboot 

# Installing magiskboot to the system PATH
sudo cp magiskboot /usr/local/bin/

03. Unpacking the boot.img

  1. Extract the boot image from your stock ROM and place it inside a new folder.

    Screenshot of the folder holding the stock boot.img

    βœ”οΈ Samsung-only note:

    • On Samsung devices, these images are usually located inside the AP_XXXX.tar.md5 file.

    • Rename AP_XXXX.tar.md5 to AP_XXXX.tar to remove the md5 extension, extract AP_XXXX.tar, and grab the boot.img.lz4 file from the extracted folder.

    • Then decompress this lz4 file with the command below, and you will get your RAW boot.img:

      lz4 boot.img.lz4

      Screenshot of lz4 decompressing boot.img.lz4

  2. Now, run the following command to unpack the boot.img:

magiskboot unpack boot.img

Screenshot of magiskboot unpacking the boot image

🟠 As you can see in the screenshot above, the original kernel of the unpacked boot.img is in the same folder as the boot.img.

Note: Don't delete the original boot.img as it is needed for the repacking process.

04. Repacking the boot.img

  • Now, all we have to do is replace the original kernel with our compiled custom kernel.

Example:

Animation of replacing the kernel inside the unpacked boot image What did I do?

  1. Copied the compiled Image from the build folder to the folder where we unpacked our boot.img using magiskboot

  2. Deleted the original kernel and renamed Image to kernel 😎

  3. Then repacked the boot.img using the below command:

magiskboot repack boot.img

Screenshot of the folder after repacking, now holding new-boot.img

🟨 Our new boot image lands in the same folder where we unpacked the stock boot.img, with the name new-boot.img.

  • Copy the new-boot.img file to another location and rename it to boot.img

  • Now, all you have to do is flash that boot.img through fastboot mode or Download mode (Samsung)

βœ”οΈ Samsung-only note:

  • You can create an ODIN-flashable tar file using the command below:

    tar -cvf "Custom-Kernel.tar" boot.img
  • Then, flash that tar file using ODIN's AP slot :)


Written by: @ravindu644 and our contributor(s)

Join Telegram: @SamsungTweaks


About

Kernel building tutorials for Any device for newbies..! πŸŽ‰

Resources

Contributing

Stars

688 stars

Watchers

15 watching

Forks

Releases

Packages

Contributors

Languages