本篇文章仅个人观点,如有错误,欢迎在评论区指正
文章内时间(当今,如今等)均以文章发布时间为准
本篇文章介绍内容以旧版内核为主(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
- 这个版本的Linux内核一般兼容 Clang 9 + GCC 4.9 的工具链。
- Clang: 可使用Google Clang,Proton Clang等,推荐使用 Clang 9 - Clang 12 等版本。
- GCC: 使用 aarch64-linux-android-gcc(来自Google),部分设备(arm架构的32位设备及部分64位设备)还需要arm-linux-androideabi-gcc(来自Google),版本推荐使用 4.9。也可以使用Linaro GCC(aarch64-linux-gnu-gcc和arm-linux-gnueabi-gcc)
Part 2: Linux 4.9
- 这几个版本的Linux内核一般兼容 Clang 12及以上 + GCC 5.3及以上 的工具链。
- Clang: 可使用Google Clang,Proton 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 LLVM,Standalone 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 | export ARCH="arm64" |
- 这个编译脚本适用于大多数 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 | export ARCH="arm64" |
- 仅三元组与 Linux 4.9及以上 不同,其余一致。
GCC
1 | export ARCH="arm64" |
- 适用于 Linux 3.x - Linux 4.4 (可能更高)
- 三元组(据工具链而定)与编译器不同。
LLVM
1 | export ARCH="arm64" |
- 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刷入)即可。