android - 编译内核模块

本页详细介绍了为 Android 设备构建自定义内核的流程。以下说明会逐步指导您如何选择正确的源代码,构建内核,以及将结果嵌入到根据 Android 开源项目 (AOSP) 构建的系统映像中。

您可以使用 Repo 获取最新的内核源代码;通过在源代码检出的根目录下运行 build/build.sh 可构建这些内核源代码,而无需更多配置。

注意:内核源代码检出的根目录包含 build/build.sh。Android 树仅包含预构建的内核二进制文件。内核树包含内核源代码和用于构建内核的所有工具,包括此脚本。

安装依赖

Ubuntu/Debian

sudo apt install binutils git ccache automake flex lzop bison gperf build-essential zip curl zlib1g-dev 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 libx11-dev libgl1-mesa-dev xsltproc unzip device-tree-compiler python3 libelf-dev binutils-aarch64-linux-gnu gcc gzip

Deepin

sudo apt install binutils git ccache automake flex lzop bison gperf build-essential zip curl zlib1g-dev libxml2-utils bzip2 libbz2-dev libbz2-1.0 squashfs-tools dpkg-dev liblz4-tool make optipng maven libssl-dev libswitch-perl policycoreutils minicom libxml-sax-base-perl libxml-simple-perl bc libx11-dev libgl1-mesa-dev xsltproc unzip device-tree-compiler python3 libelf-dev binutils-aarch64-linux-gnu gcc gzip

deepin环境下可能需要单独手动编译安装一些依赖库!

安装repo

大部分情况下,apt install repo都无法找到包,所以说我们进行手动安装!

mkdir -p ~/.bin
PATH="${HOME}/.bin:${PATH}"
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
chmod a+rx ~/.bin/repo

因为墙的问题(如果您的网络无法访问Google),需要更换源,这里更换为清华源

export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'

下载内核编译环境

mkdir android-kernel && cd android-kernel

先创建一个文件夹让我们可以进行repo init

repo init -u https://android.googlesource.com/kernel/manifest -b common-[需要的版本] --repo-rev=v2.16

common-开头,后面任你选择哦!具体有哪些tag,我们可以看https://android.googlesource.com/kernel/manifest/+refs

repo sync -c -j$(nproc) --no-tags

需要注意的是,这个拉取同步的速度取决于你的网速,

我的网络就很慢很慢!

如需查看可与之前的“repo init”命令搭配使用的 repo 分支 (BRANCH) 列表,请参阅内核分支及其构建系统

如需详细了解如何为 Pixel 设备下载和编译内核,请参阅构建 Pixel 内核

将需要编译的内核模块挪动位置

GKI_ROOT=/persistent/home/fuqiuluo/etc/env/android-kernel/android15-6.6
mv [模块路径] $GKI_ROOT/common/drivers/[模块名称]
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
grep -q "[模块名称]" "$DRIVER_MAKEFILE" || printf "\nobj-m += [模块名称]/\n" >> "$DRIVER_MAKEFILE"

如果内核模块内有Kconfig文件则还需要执行

grep -q "[模块名称]" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/[模块名称]/Kconfig\"" "$DRIVER_KCONFIG"

编译前准备

sudo apt-get install llvm-15 -y
pip install ast-grep-cli

接下来我们需要使用这个ast-grep包,但是你可能安装后无法直接使用请手动设置PATH或者使用cargo包管理器进行安装,cargo install ast-grep --locked !安装成功后,我们逐行执行以下命令:

ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c

如果编译的时候报错提示什么和common/scripts/mod/modpost.c 有关的错误,说明上面的这段命令执行没有效果,需要你手动去对应文件删除和check_exports相关的定义和调用!

检查build/build.sh文件是否存在,如果不存在则执行以下语句:

sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"

高版本内核添加内核模块(上面的执行完成后,我们继续执行,如果文件build/build.sh不存在且文件common/modules.bzl存在则执行以下语句)

sed -i "s/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ \"drivers\/[模块名称]\/[模块名称].ko\",/g" common/modules.bzl

低版本内核去除一些检测(如果build/build.sh存在的话)

export TARGET_FILE="build/kernel/build.sh"
if [ ! -f "$TARGET_FILE" ]; then
    TARGET_FILE="build/build.sh"
fi
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
sed -i 's@\${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"

我们可以输入repo status 看看修改了哪些文件,同时我们需要执行以下语句删点东西(低版本内核没有这些玩意?):

rm common/android/abi_gki_protected_exports_*

构建编译

使用构建脚本构建内核(如果build/build.sh存在)

注意:Android 14 及更高版本不支持 build.sh。

对于 Android 12 或更低版本的分支,或者不使用 Kleaf 的分支:

LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"

后记

在一次编译安卓12的MUMU模拟器的5.4版本的内核的时候,遇到了错误,但是这个版本是用build.sh构建的就找不到错误信息...(被bazel惯坏了),

构建脚本换成LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh V=1 CC="/usr/bin/ccache clang" 2>&1 | tee build.log 然后我们去这个Build.log里面找一下common/drivers/[模块名称] 找一下详细错误信息...

输出目录在out/android12-5.4/common/drivers/$MODULE_NAME/$MODULE_NAME.ko

使用 Bazel (Kleaf) 构建(不存在则执行)

tools/bazel run --config=fast --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist

如果你需要编译X64的,可以把aarch64改成x86_64

如果您要构建tag为android16-6.12的内核模块,请使用tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --lto=thin //common:kernel_aarch64_dis

Android 13 引入了使用 Bazel 构建内核的功能。

如需为 aarch64 架构构建 GKI 内核,请查看不低于 Android 13 的 Android 通用内核分支,然后运行以下命令:

tools/bazel build //common:kernel_aarch64_dist

如需创建分发版本,请运行以下命令:

tools/bazel run //common:kernel_aarch64_dist -- --dist_dir=$DIST_DIR

之后,内核二进制文件、模块和相应的映像会置于 $DIST_DIR 目录中。如果未指定 --dist_dir,请参阅该命令的输出以了解工件的位置。如需了解详情,请参阅 AOSP 文档

编译输出的内核模块就在那个叫dist的文件夹里面啦!编译完成的时候,他也会说放到什么地方去的哦!

参考