在 AVD 上使用 KernelSU
在 AVD 上使用 KernelSU
曾经尝试过给 AVD 构建带有 KernelSU 的内核,方便开发测试,但是发现内核模块不兼容,现在的方法是同时构建内核模块,略为复杂。
构建内核
https://source.android.com/docs/setup/build/building-kernels?hl=zh-cn
Android 14 的 AVD 内核版本:
1 | Linux version 6.1.23-android14-4-00257-g7e35917775b8-ab9964412 (build-user@build-host) (Android (9796371, based on r487747) clang version 17.0.0 (https://android.googlesource.com/toolchain/llvm-project d9f89f4d16663d5012e5c09495f3b30ece3d2362), LLD 17.0.0) #1 SMP PREEMPT Mon Apr 17 20:50:58 UTC 2023 |
根据 KernelSU CI 进行操作,拉取对应版本源码
1 | repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-android14-6.1-2023-12 --repo-rev=v2.16 |
同步完成后可以用 bazel 构建
1 | tools/bazel build //common:kernel_x86_64_dist |
产物位置: bazel-out/k8-fastbuild/bin/common/kernel_x86_64/bzImage
解决模块不兼容
上面构建出来的内核与 AVD 原镜像中的模块不兼容,按理来说 GKI 是确保了模块接口稳定性的,虽然我们的 KMI Generation 不同。总之需要构建对应的模块。
有三个地方的模块会被加载:ramdisk ,/system(_dlkm)/lib/modules , /vendor/lib/modules 。
构建
上面执行的 dist 已经构建好了 system_dlkm ,输出在 bazel-out/k8-fastbuild/bin/common/kernel_x86_64/unstripped/
(其实应该有一个 dist 目录,不过我没指定,也没找到默认的在哪)。
我们还需要构建 vendor (以及 boot )的 ko 。根据文档,执行 tools/bazel run //common-modules/virtual-device:virtual_device_x86_64_dist
即可。输出在 out/virtual_device_x86_64/dist/
ramdisk
使用 magiskboot 查看 ramdisk ,发现在 lib/modules 下。
1 | /data/adb/magisk/magiskboot decompress ramdisk.img ramdisk.cpio |
因此构建好模块后,只需要把新的 ko 添加到 cpio 即可。
1 | /data/adb/magisk/magiskboot decompress ramdisk.img ramdisk.cpio |
需要用到的 ko 大概是这些:
1 | cp out/virtual_device_x86_64/dist/{virtio-rng.ko,virtio_blk.ko,virtio_console.ko,virtio_dma_buf.ko,virtio_pci.ko,virtio_pci_legacy_dev.ko,virtio_pci_modern_dev.ko,vmw_vsock_virtio_transport.ko} /mnt/d/Documents/tmp/kmods |
system & vendor
观察发现 /system/lib/modules/modules.load 是空的,只有 /vendor/lib/modules 有。于是写一个脚本解析需要的 ko ,并从构建好的 ko 中搜索,复制出来。
1 | import os |
之后需要把构建好的 ko 放到 /system_dlkm 和 /vendor ,我们需要开启 avd 的 writable system ,这样会使用 overlayfs 提供一个可写的叠加层。
1 | # 启动 writable system |
首次启用需要先 adb remount 并重启才生效,之后如果启动 avd 指定了 -writable-system 才会使用叠加层,不指定则不会。
之后就可以直接写这些目录了,通过 adb push 推送 ko 即可。
KernelSU
也是参考 CI 的方式,注意构建内核必须用 CI 的命令,否则无法正确找到 KernelSU 版本。似乎是默认的构建方式用了 sandbox ,导致 Makefile 中 srctree 传入的路径不对。
1 | tools/bazel run --config=fast --config=stamp --lto=thin //common:kernel_x86_64_dist -- --dist_dir=dist |
指定了 dist 目录后,构建好的内核(bzImage)及 system_dlkm 都在 dist
目录下。
启动
带着构建好的内核和 patch 的 ramdisk 启动:
1 | emulator @API34 -kernel D:\Documents\tmp\kmods\bzImage -no-snapshot-load -show-kernel -writable-system -ramdisk D:\Documents\tmp\kmods\ramdisk.gz |
总结
虽然 KernelSU 支持 arm 和 x86,但自动构建一直以来只有 arm ,因此在 x86 上使用只能靠自己。像笔者这样坚持在 x86 上使用 KernelSU 也许是少数人,(也许你们不信 前 ZygiskOnKernelSU 现 ZygiskNext 的主要开发者开发和测试这个模块的主要环境是 AVD 上的 Magisk ,而 Magisk + ZygiskNext 是不推荐的,)毕竟笔者既没有新手机也没有 M1 M2 只能在 x86 PC 上跑模拟器来研究真是抱歉呢。总之,作为 x86 模拟器用户想用 KernelSU 只有两种选择,无论是 cuttlefish 还是 AVD ,集成和使用体验都很痛苦。
最近 KernelSU 正在筹划模块化的实现,如果能用于 AVD 将可以减少这种痛苦,值得一试。