构建内核

本页面详细介绍了为 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