本页面详细介绍了为 Android 设备构建自定义内核的流程。这些说明将指导您完成选择正确的源代码、构建内核以及将结果嵌入到从 Android 开源项目 (AOSP) 构建的系统映像中的过程。
您可以使用 Repo 获取更新的内核源代码;通过从源代码检出的根目录运行 build/build.sh
即可构建它们,无需进一步配置。
下载源代码和构建工具
对于最新的内核,请使用 repo
下载源代码、工具链和构建脚本。某些内核(例如,Pixel 3 内核)需要来自多个 git 代码库的源代码,而其他内核(例如,通用内核)仅需要单个源代码。使用 repo
方法可确保正确的源代码目录设置。
下载相应分支的源代码
mkdir android-kernel && cd android-kernel
repo init -u https://android.googlesource.com/kernel/manifest -b BRANCH
repo sync
如需查看可用于之前 `repo init` 命令的 repo 分支 (BRANCH) 列表,请参阅内核分支及其构建系统。
如需详细了解如何下载和编译 Pixel 设备的内核,请参阅构建 Pixel 内核。
构建内核
使用 Bazel (Kleaf) 构建
Android 13 引入了使用 Bazel 构建内核。
要为 aarch64 架构的 GKI 内核创建分发,请检出不早于 Android 13 的 Android 通用内核分支,然后运行以下命令
tools/bazel run //common:kernel_aarch64_dist [-- --destdir=$DIST_DIR]
之后,内核二进制文件、模块和相应的映像将位于 $DIST_DIR
目录中。如果未指定 --destdir
,请参阅命令的输出,了解工件的位置。如需了解详情,请参阅 AOSP 上的文档。
使用 build.sh 构建(旧版)
对于 Android 12 或更低版本的分支,或没有 Kleaf 的分支
build/build.sh
内核二进制文件、模块和相应的映像位于 out/BRANCH/dist
目录中。
为虚拟设备构建供应商模块
Android 13 引入了使用 Bazel (Kleaf) 构建内核,取代了 build.sh
。
要为 virtual_device
的模块创建分发,请运行
tools/bazel run //common-modules/virtual-device:virtual_device_x86_64_dist [-- --destdir=$DIST_DIR]
如需详细了解如何使用 Bazel 构建 Android 内核,请参阅。Kleaf - 使用 Bazel 构建 Android 内核。
如需详细了解 Kleaf 对各个架构的支持,请参阅Kleaf 对设备和内核的支持。
使用 build.sh 构建虚拟设备的供应商模块(旧版)
在 Android 12 中,Cuttlefish 和 Goldfish 已融合,因此它们共享相同的内核:virtual_device
。要构建该内核的模块,请使用此构建配置
BUILD_CONFIG=common-modules/virtual-device/build.config.virtual_device.x86_64 build/build.sh
Android 11 引入了 GKI,它将内核分为 Google 维护的内核映像和供应商维护的模块,这些模块是单独构建的。
此示例显示了内核映像配置
BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh
此示例显示了模块配置(Cuttlefish 和模拟器)
BUILD_CONFIG=common-modules/virtual-device/build.config.cuttlefish.x86_64 build/build.sh
运行内核
有多种方法可以运行自定义构建的内核。以下是适用于各种开发场景的已知方法。
嵌入到 Android 映像构建中
将 Image.lz4-dtb
复制到 AOSP 树中相应的内核二进制文件位置,然后重新构建启动映像。
或者,在使用 make bootimage
(或任何其他构建启动映像的 make
命令行)时,定义 TARGET_PREBUILT_KERNEL
变量。所有设备都支持此变量,因为它通过 device/common/populate-new-device.sh
进行设置。例如
export TARGET_PREBUILT_KERNEL=DIST_DIR/Image.lz4-dtb
使用 fastboot 刷写和启动内核
大多数最新设备都具有启动加载程序扩展,以简化生成和启动启动映像的过程。
要启动内核而不刷写
adb reboot bootloader
fastboot boot Image.lz4-dtb
使用此方法,内核实际上并未刷写,并且不会在重启后保留。
在 Cuttlefish 上运行内核
您可以在Cuttlefish 设备上运行您选择的架构中的内核。
要使用特定的一组内核工件启动 Cuttlefish 设备,请使用目标内核工件作为参数运行 cvd create
命令。以下示例命令使用来自 common-android14-6.1
内核清单的 arm64 目标的内核工件。
cvd create \
-kernel_path=/$PATH/$TO/common-android14-6.1/out/android14-6.1/dist/Image \
-initramfs_path=/$PATH/$TO/common-android14-6.1/out/android14-6.1/dist/initramfs.img
如需了解更多信息,请参阅在 Cuttlefish 上开发内核。
自定义内核构建
要自定义 Kleaf 构建的内核构建,请参阅 Kleaf 文档。
使用 build.sh 自定义内核构建(旧版)
对于 build/build.sh
,构建过程和结果可能会受到环境变量的影响。其中大多数是可选的,每个内核分支都应附带正确的默认配置。此处列出了最常用的环境变量。如需获取完整(且最新)的列表,请参阅 build/build.sh
。
环境变量 | 描述 | 示例 |
---|---|---|
BUILD_CONFIG |
从中初始化构建环境的构建配置文件。位置必须相对于 Repo 根目录定义。默认为 build.config 。对于通用内核是必需的。 |
BUILD_CONFIG=common/build.config.gki.aarch64 |
CC |
替换要使用的编译器。回退到 build.config 定义的默认编译器。 |
CC=clang |
DIST_DIR |
内核分发的基本输出目录。 | DIST_DIR=/path/to/my/dist |
OUT_DIR |
内核构建的基本输出目录。 | OUT_DIR=/path/to/my/out |
SKIP_DEFCONFIG |
跳过 make defconfig |
SKIP_DEFCONFIG=1 |
SKIP_MRPROPER |
跳过 make mrproper |
SKIP_MRPROPER=1 |
本地构建的自定义内核配置
在 Android 14 及更高版本中,您可以使用 defconfig 片段自定义内核配置。请参阅 Kleaf 文档,了解 defconfig 片段。
使用构建配置的本地构建的自定义内核配置(旧版)
在 Android 13 及更低版本中,请参阅以下内容。
如果您需要定期切换内核配置选项(例如,在处理某个功能时),或者如果您需要为开发目的设置某个选项,则可以通过维护本地修改或构建配置的副本来实现这种灵活性。
将变量 POST_DEFCONFIG_CMDS 设置为在完成通常的 make defconfig
步骤后立即评估的语句。由于 build.config
文件已添加到构建环境中,因此可以在 post-defconfig 命令中调用在 build.config
中定义的函数。
一个常见的示例是在开发期间禁用 crosshatch 内核的链接时优化 (LTO)。虽然 LTO 对发布的内核很有用,但构建时的开销可能很大。添加到本地 build.config
的以下代码段在使用 build/build.sh
时会持久禁用 LTO。
POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
function update_debug_config() {
${KERNEL_DIR}/scripts/config --file ${OUT_DIR}/.config \
-d LTO \
-d LTO_CLANG \
-d CFI \
-d CFI_PERMISSIVE \
-d CFI_CLANG
(cd ${OUT_DIR} && \
make O=${OUT_DIR} $archsubarch CC=${CC} CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
}
识别内核版本
您可以从两个来源识别要构建的正确版本:AOSP 树和系统映像。
来自 AOSP 树的内核版本
AOSP 树包含预构建的内核版本。git 日志在提交消息中显示了正确的版本
cd $AOSP/device/VENDOR/NAME
git log --max-count=1
如果 git 日志中未列出内核版本,请从系统映像中获取,如下所述。
来自系统映像的内核版本
要确定系统映像中使用的内核版本,请针对内核文件运行以下命令
file kernel
对于 Image.lz4-dtb
文件,请运行
grep -a 'Linux version' Image.lz4-dtb
构建启动映像
可以使用内核构建环境构建启动映像。
为具有 init_boot 的设备构建启动映像
对于具有init_boot
分区的设备,启动映像与内核一起构建。initramfs
映像未嵌入到启动映像中。
例如,使用 Kleaf,您可以使用以下命令构建 GKI 启动映像
tools/bazel run //common:kernel_aarch64_dist [-- --destdir=$DIST_DIR]
使用 build/build.sh
(旧版),您可以使用以下命令构建 GKI 启动映像
BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
GKI 启动映像位于 $DIST_DIR 中。
为没有 init_boot 的设备构建启动映像(旧版)
对于没有init_boot
分区的设备,您需要一个 ramdisk 二进制文件,您可以通过下载 GKI 启动映像并解压缩它来获得。来自关联的 Android 版本的任何 GKI 启动映像都可以使用。
tools/mkbootimg/unpack_bootimg.py --boot_img=boot-5.4-gz.img
mv $KERNEL_ROOT/out/ramdisk gki-ramdisk.lz4
目标文件夹是内核树的顶层目录(当前工作目录)。
如果您正在使用 AOSP main 进行开发,则可以从 ci.android.com 上的 aosp_arm64 构建下载 ramdisk-recovery.img
构建工件,并将其用作您的 ramdisk 二进制文件。
当您拥有 ramdisk 二进制文件并将其复制到内核构建根目录中的 gki-ramdisk.lz4
时,您可以通过执行以下命令生成启动映像
BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=Image GKI_RAMDISK_PREBUILT_BINARY=gki-ramdisk.lz4 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
如果您正在使用基于 x86 的架构,请将 Image
替换为 bzImage
,并将 aarch64
替换为 x86_64
BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=bzImage GKI_RAMDISK_PREBUILT_BINARY=gki-ramdisk.lz4 BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh
该文件位于工件目录 $KERNEL_ROOT/out/$KERNEL_VERSION/dist
中。
启动映像位于 out/<kernel branch>/dist/boot.img
。