本篇文章仅个人观点,如有错误,欢迎在评论区指正

文章内时间(当今,如今等)均以文章发布时间为准

本篇文章介绍内容以旧版内核为主(4.4,4.9,4.19),可能存在过时内容,请仔细甄别

  • 随着刷机的迅速发展,人们已经不满足于用户态,而是向着内核空间进军,当今的众多项目(如KernelSU,Re:Kernel,RapidGC,SimpleLMK等)均建立在对内核的修改之上。“内核级玩机”仿佛成了一种潮流,迅速发展着。
  • 本篇文章,我将介绍Android内核(4.4,4.9,4.19等 非GKI/GKI 1.0内核)的编译方法,顺便为部分内容做出解析。

    玩机有风险,刷机需谨慎!

    由此造成的一切不良后果均由自己承担!

准备环境

  • 编译内核,需要一个合适的系统来编译,并在这个系统中做好编译的准备,我们称之为“准备环境”。

选择Linux

  • 编译一个Linux内核,我们同样需要一个Linux环境。
  • Linux的发行版有很多种,但主要是 Debian系(如Ubuntu,Kali等),Redhat系(如CentOS,Fedora等),Arch系(如Manjaro,BlackArch等),其中,最适合用于Linux内核编译的发行版是Ubuntu。(如果你有足够的经验或足够铁的头,也可以像我一样用ArchLinux编译(?))
  • 至于具体怎么安装Ubuntu就不提了,本篇教程也将以Ubuntu为例,介绍编译安卓内核的基本步骤。
  • Ubuntu 建议安装 22.04 (编译旧内核足矣),如果有特殊需求(如使用SukiSU的KernelPatch)可以安装更新的版本

安装所需软件包

  • 编译Linux内核需要安装一些软件。
  • 这里以Ubuntu 24.04为例,使用以下命令安装所需软件包:
    sudo apt-get install git automake flex lzop bison gperf build-essential zip curl zlib1g-dev g++-multilib gcc-multilib libxml2-utils bzip2 libbz2-dev libbz2-1.0 libghc-bzlib-dev squashfs-tools pngcrush schedtool dpkg-dev liblz4-tool make optipng maven libssl-dev pwgen libswitch-perl policycoreutils minicom libxml-sax-base-perl libxml-simple-perl bc libc6-dev-i386 libx11-dev lib32z-dev libgl1-mesa-dev xsltproc unzip device-tree-compiler python3 ccache git-lfs gnupg imagemagick libelf-dev libncurses5-dev libsdl1.2-dev rsync adb fastboot libstdc++6 -y

工具链

  • 编译一个Linux内核,需要一些特定的编译器。
  • 然而,由于技术的进步,时代的发展,许多新编译器已经不能很好的兼容旧Linux内核源码,所以我们就需要使用 同时代的/特定的 编译器以保证兼容性,我们称之为工具链。
  • 以下是编译Android内核常用的几种工具链组合(均以Linux-x86为编译主机):

Clang + GCC

Part 1: Linux 4.4

Part 2: Linux 4.9

  • 这几个版本的Linux内核一般兼容 Clang 12及以上 + GCC 5.3及以上 的工具链。
  • Clang: 可使用Google ClangProton Clang等,推荐使用 Clang 12及以上 版本(不要太新)。
  • GCC: 使用aarch64-linux-gnu-gcc,部分设备(arm架构的32位设备及部分64位设备)还需要arm-linux-gnueabi-gcc,可以使用Linaro GCC(aarch64-linux-gnu-gcc和arm-linux-gnueabi-gcc),推荐使用 5.3及以上 版本(不要太新)。

    Linux 4.14 和 Linux 4.19 与 Linux 4.9 类似,但版本略新。

GCC

  • 通常,Linux 3.x - Linux 4.4 的内核都使用纯GCC编译。
  • 使用 aarch64-linux-android-gcc(来自Google),部分设备(arm架构的32位设备及部分64位设备)还需要arm-linux-androideabi-gcc(来自Google),版本推荐使用 4.9。

LLVM

  • 通常,使用 Linux 4.19 及以上的安卓内核支持使用LLVM编译。一般使用最新版本就好,如兼容性不佳,可尝试使用旧版本或使用 Clang + GCC 组合。
  • LLVM: 可使用 Google LLVMStandalone LLVM 等。

Python 2 (可选)

  • 通常,在使用 Linux 4.19 的安卓内核需要使用Python2。然而,由于Python2生命周期的结束,在Ubuntu 23.10及以上版本的Ubuntu中,在软件源里移除了Python2。所以,在使用Ubuntu 23.10及以上版本Ubuntu的宿主机编译4.19内核时,可能需要准备Python2工具链。(如果使用Ubuntu 23.10以下版本,可以直接通过包管理器安装Python2,无需额外准备。)
  • Python2: 可使用 Python Prebuit

拉取内核源码及工具链

  • 编译一个内核,必须要有源码。
  • 小米的官方源码可以在MiCode获取,且小米12之后的设备源码不全,其他厂商只公开了部分设备的源码
  • 建议使用第三方源码(如LineageOS,或其他第三方内核),可能会失去部分官方特性,但便于编译,可以少走很多弯路
  • 在你的Ubuntu中找一个有权限访问的目录,如home,然后使用以下命令拉取源码:
    git clone <你的源码仓库地址> --depth 1
  • 同样在有权限访问的目录,根据上文选择合适的工具链,随后使用以下命令拉取:
    git clone <你的工具链仓库地址> --depth 1
  • 当然了,获取源码和工具链的方式有很多种,不要局限于这串命令,如果不行,就换一种方法,教程是死的,可人是活的。
  • 最后记好工具链的位置,进入下一步。

编写编译脚本

  • 编译一个内核,要设置很多环境变量。但如果在每一次编译的时候都手动设置,可就有点麻烦了。通过cd命令进入源码目录,创建一个名为“build.sh”的脚本(名字是什么无所谓,只要是.sh格式就行)。在这里,我将举例几种编译脚本:

Clang + GCC

Part 1: Linux 4.9及以上

1
2
3
4
5
6
7
8
9
export ARCH="arm64"
export SUBARCH="arm64"
export PATH="/root/toolchain/clang/bin:/root/toolchain/gcc32/bin:/root/toolchain/gcc64/bin:$PATH"
export CC="clang"
export CROSS_COMPILE="aarch64-linux-gnu-"
export CROSS_COMPILE_ARM32="arm-linux-gnueabi-"
export TRIPLE="aarch64-linux-gnu-"
make O=out <内核配置文件>
make -j$(nproc --all) O=out
  • 这个编译脚本适用于大多数 Linux 4.9及以上 的内核。
  • ARCH: 设备架构
  • SUBARCH: 同上
  • PATH: Linux主机中的PATH,在系统PATH($PATH)设置你的工具链(拉取的编译器在工具链中的位置,通常为各工具链的bin目录)的PATH。这里只是举例,不要照搬!

    PATH是Linux中的一个重要环境变量,linux中执行的命令(如ls,mkdir,git)等都在PATH指定的文件夹(如/usr/bin,/bin)中,在这里,通过设置PATH,可以使编译程序找到编译器的位置。

    比如,/root/toolchain/clang/bin就是clang编译器的二进制文件所在位置,设置PATH后make可以直接调用clang

    同时,PATH是有先后顺序的,在这里,每个编译器的PATH不能在系统PATH($PATH)之后,不然make会优先调用系统中的编译器,工具链就没被用到了,多个PATH之间用“:”隔开。

  • CC: 编译内核使用的编译器
  • CROSS_COMPILE: 交叉编译工具链三元组

    在上面,我们已经设置了PATH,编译程序make调用的是gcc,但我们拉取的工具链是aarch64-linux-gnu-gcc,这时为了调用aarch64-linux-gnu-gcc,将CROSS_COMPILE设置为aarch64-linux-gnu-,编译程序就会调用aarch64-linux-gnu-gcc而不是gcc了,也就是设置目标三元组。

  • CROSS_COMPILE_ARM32(可选): 32位交叉编译工具链三元组

    在部分内核上需要编译32位的东西,这时就需要设置CROSS_COMPILE_ARM32来调用之前我们准备的arm-linux-gnueabi-gcc工具链了。同CROSS_COMPILE

  • TRIPLE: 交叉编译工具链三元组
  • 以上是编译内核时需要设置的几个环境变量。
  • make O=out <内核配置文件>: 写入内核配置

    编译Linux内核需要有一个配置文件。在arm64架构中,它通常在arch/arm64/configs/目录下,使用这个命令也会直接调用这个目录下的配置文件。

    这个<内核配置文件>应该设置为你的机型在arch/arm64/configs/目录下对应的配置文件。

    O=out是设置编译输出目录。

  • make -j$(nproc –all) O=out: 开始编译

    -j$(nproc –all)是让make使用电脑所有的核心进行编译,可以提高编译速度。

  • 其中需要手动更改的部分为PATH

Part 2: Linux 4.4

1
2
3
4
5
6
7
8
9
export ARCH="arm64"
export SUBARCH="arm64"
export PATH="/root/toolchain/clang/bin:/root/toolchain/gcc32/bin:/root/toolchain/gcc64/bin:$PATH"
export CC="clang"
export CROSS_COMPILE="aarch64-linux-android-"
export CROSS_COMPILE_ARM32="arm-linux-androideabi-"
export TRIPLE="aarch64-linux-android-"
make O=out <内核配置文件>
make -j$(nproc --all) O=out
  • 仅三元组与 Linux 4.9及以上 不同,其余一致。

GCC

1
2
3
4
5
6
7
8
9
export ARCH="arm64"
export SUBARCH="arm64"
export PATH="/root/toolchain/gcc32/bin:/root/toolchain/gcc64/bin:$PATH"
export CC="gcc"
export CROSS_COMPILE="aarch64-linux-android-"
export CROSS_COMPILE_ARM32="arm-linux-androideabi-"
export TRIPLE="aarch64-linux-android-"
make O=out <内核配置文件>
make -j$(nproc --all) O=out
  • 适用于 Linux 3.x - Linux 4.4 (可能更高)
  • 三元组(据工具链而定)与编译器不同。

LLVM

1
2
3
4
5
6
7
8
9
10
export ARCH="arm64"
export SUBARCH="arm64"
export PATH="/root/toolchain/llvm/bin:/root/toolchain/python2/bin:$PATH"
export CC="clang"
export LLVM=1
export LLVM_IAS=1
export CLANG_TRIPLE="aarch64-linux-gnu-"
export CROSS_COMPILE="aarch64-linux-gnu-"
make O=out <内核配置文件>
make -j$(nproc --all) O=out
  • LLVM: 启用LLVM编译
  • LLVM_IAS: 使用Clang内部编译器
  • CLANG_TRIPLE, CROSS_COMPILE: 设置三元组

    在4.19及以上内核中可能没什么必要(我之前设置错了,但一直没出任何问题),但不设置不行(有判断)

  • 其他同上。

编译内核

  • 在做好了万全的准备后,我们就该开始编译内核了。
  • cd到源码目录,执行以下命令为编译脚本赋予可执行权限:
    `` chmod +x build.sh ‘’
  • 然后就要正式开始了:
    bash build.sh
  • 如果没有意外,编译就应该通过了。

    有意外自己修,每个源码都不一样,不可能一次性讲清,还是那句话,教程是死的,可人是活的。编译Linux也是一种学习的过程,不能都指望着别人。

打包内核并刷入

  • 在编译通过(既没有任何报错信息)后,成品内核会生成在out/arch/arm64/boot/目录下,文件名通常为Image。

    这个目录下部分源码只会生成Image,大部分源码还会生成Image.gz,Image-dtb,Image.gz-dtb。

    Image为内核成品,dtb则是设备树文件,其中存放着有关设备硬件的重要信息,Image-dtb和Image.gz-dtb等内核包含了dtb文件,打包时只需要打包Image-dtb或Image-gz-dtb即可,否则还需要打包out/arch/arm64/boot/dts/目录下的dtb文件。

  • 打包还需要用到Anykernel3。使用以下命令拉取Anykernel3:
    git clone https://github.com/osm0sis/AnyKernel3 --depth 1 anykernel3
  • 随后将内核复制到anykernel3目录下:

如果生成了Image-dtb或Image.gz-dtb:

cp out/arch/arm64/boot/Image-dtb anykernel3/

  • 或:

cp out/arch/arm64/boot/Image.gz-dtb anykernel3/

如果只生成了Image:

cp out/arch/arm64/boot/Image anykernel3/
cp out/arch/arm64/boot/dts/*/*.dtb anykernel3/

需要自己确定dtb文件的路径,每个内核都不一样。

  • 随后cd进入anykernel3目录,修改anykernel.sh:
  • 将do.devicecheck的值改为0,
  • 如果手机是AB或VAB分区设备,则将IS_SLOT_DEVICE改为1,如果是A-Only分区设备,则改为0。
  • 最后一步,打包。cd进anykernel3目录,执行以下命令:
    zip -r anykernel3.zip *
  • 随后将生成的anykernel3.zip文件在Recovery模式中刷入(或使用KernelFlasher刷入)即可。