【Android】基于AOSP源码编译Pixel3的boot.img(下)
【Android】基于AOSP源码为Pixel3编译boot.img(下)简介
本系列主要是为了实现将AndroidKernel源码划分为AOSP源码的目标,使得前者的编译框架可以直接从源码编译出boot.img。 上一节我们已经基本完成了编译框架的整合;
本文将在上一节的基础上讨论一些改进和优化。 同时我们会在CAF(CodeAuroraForum,现修改为:CodeLinaro)讨论SDM845(Pixel3SoC)+Android9.0的baseline,以及Pixel3(blueline)在LineageOS16(Android9.0)上对应的baseline,两者这对于从源代码编译引导很重要。 img的目标实现上有哪些差异,好坏如何;
环境桌面
Ubuntu16.04
AOSP版本
9.0(安卓-9.0.0_r46)
内核版本
4.9(android-msm-crosshatch-4.9-pie-qpr2)
像素型号
像素3(4GB+64GB)
主体编译规则改进代码结构优化
因为我们收到Android内核编译产品后会调整其存储目录,所以建议提取一个宏来控制之前硬编码的kernel/out目录部分:
# BoardConfig.mk或等效路径下添加:
KERNEL_OUT := kernel/out
然后更改上一篇文章中定义内核伪目标的 kernel.mk 文件(或等效目录):
# 对使用kernel/out目录的部分进行替换
$(PRODUCT_OUT)/kernel: $(SOONG_ZIP)
@echo "Making kernel"
cd kernel && bash build/build.sh
cp $(KERNEL_OUT)/dist/*.ko $(TARGET_OUT_VENDOR)/lib/modules/
cp $(KERNEL_OUT)/dist/dtbo.img $(PRODUCT_OUT)/dtbo.img
cp $(KERNEL_OUT)/dist/Image.lz4-dtb $(PRODUCT_OUT)/kernel
.PHONY: kernel
kernel: $(PRODUCT_OUT)/kernel
# 这里也修改以下:kernelclean删除目录时,细化到每个子目录,而不是整个$(KERNEL_OUT)直接删除
.PHONY: kernelclean
kernelclean:
if [[ ! -z `find $(TARGET_OUT_VENDOR)/lib/modules -name "*.ko"` ]]; then rm $(TARGET_OUT_VENDOR)/lib/modules/*.ko; fi
if [ -f $(PRODUCT_OUT)/dtbo.img ]; then rm $(PRODUCT_OUT)/dtbo.img; fi
if [ -f $(PRODUCT_OUT)/kernel ]; then rm $(PRODUCT_OUT)/kernel; fi
if [ -d $(KERNEL_OUT)/dist ]; then rm -rf $(KERNEL_OUT)/dist; fi
if [ -d $(KERNEL_OUT)/kernel_uapi_headers ]; then rm -rf $(KERNEL_OUT)/kernel_uapi_headers; fi
if [ -d $(KERNEL_OUT)/private ]; then rm -rf $(KERNEL_OUT)/private; fi
if [ -d $(KERNEL_OUT)/staging ]; then rm -rf $(KERNEL_OUT)/staging; fi
@echo "kernelclean done"
应对问题
转录成Makefile句型
#指定统一的绝对输出路径
KERNEL_OUT := $(PWD)/$(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ
#指定内核源码存放的绝对路径
KERNEL_DIR := $(PWD)/kernel/private/msm-google
#需要编译的外部模块的绝对路径
EXT_MODULES := $(PWD)/kernel/private/msm-google-modules/wlan/qcacld-3.0
#编译参考的defconfig文件名
DEFCONFIG := b1c1_defconfig
#编译参数指定为clang
CC_ARG := "CC=clang"
#环境变量PATH添加
KERNEL_BUILD_PATH := $(strip $(PWD)/prebuilts/clang/host/linux-x86/clang-4393122/bin)
KERNEL_BUILD_PATH := $(strip $(KERNEL_BUILD_PATH):$(PWD)/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin)
KERNEL_BUILD_PATH := $(strip $(KERNEL_BUILD_PATH):$(PWD)/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin)
#将需要的变量、参数,统一添加到KERNEL_BUILD_ENV中
KERNEL_BUILD_ENV :=
PATH=$(KERNEL_BUILD_PATH):$$PATH
OUT_DIR=$(KERNEL_OUT)
ANDROID_SOURCE_ROOT=$(PWD)
ROOT_DIR=$(PWD)/kernel
KERNEL_DIR=$(KERNEL_DIR)
UNSTRIPPED_MODULES="wlan.ko"
ARCH=arm64
CROSS_COMPILE=aarch64-linux-android-
CROSS_COMPILE_ARM32=arm-linux-androideabi-
LD_LIBRARY_PATH=$(PWD)/prebuilts/clang/host/linux-x86/clang-4393122/lib64:$(LD_LIBRARY_PATH)
IN_KERNEL_MODULES=1
STOP_SHIP_TRACEPRINTK=1
CLANG_TRIPLE=aarch64-linux-gnu-
build-defconfig:
echo "========================================================"
echo " Setting up for build"
mkdir -p $(KERNEL_OUT)
@$(MAKE) -C ${KERNEL_DIR} O=$(KERNEL_OUT) ${DEFCONFIG} $(KERNEL_BUILD_ENV)
build-kernel: build-defconfig
echo "========================================================"
echo " Building kernel"
@$(KERNEL_BUILD_ENV)
$(MAKE) -C $(KERNEL_OUT) O=$(KERNEL_OUT) ${CC_ARG} -j$(shell nproc)
#内部模块(源码在KERNEL_DIR中)
build-internal-kernel-modules: build-kernel
rm -rf $(KERNEL_OUT)/staging
mkdir -p $(KERNEL_OUT)/staging
echo "========================================================"
echo " Installing kernel modules into staging directory"
@$(KERNEL_BUILD_ENV)
$(MAKE) -C $(KERNEL_OUT) O=$(KERNEL_OUT) ${CC_ARG} INSTALL_MOD_STRIP=1
INSTALL_MOD_PATH=$(KERNEL_OUT)/staging modules_install
#外部模块(源码不在KERNEL_DIR中)
define build-external-kernel-module-internal
mkdir -p $(1);
@$(KERNEL_BUILD_ENV)
$(MAKE) -C $(1) M=$(1) KERNEL_SRC=$(KERNEL_DIR)
O=$(KERNEL_OUT) $(CC_ARG) -j$(shell nproc);
@$(KERNEL_BUILD_ENV)
$(MAKE) -C $(1) M=$(1) KERNEL_SRC=$(KERNEL_DIR)
O=$(KERNEL_OUT) $(CC_ARG) INSTALL_MOD_STRIP=1
INSTALL_MOD_PATH=$(KERNEL_OUT)/staging modules_install;
endef
build-external-kernel-module: build-kernel
echo "========================================================"
echo " Building external modules and installing them into staging directory"
$(foreach ext_mod,$(EXT_MODULES),
$(call build-external-kernel-module-internal,$(ext_mod)))
去掉内核/构建仓库验证-正常编译
问题:链接阶段在单线程中执行,耗时较长
解决办法:调试时不要启用以下宏:
# CONFIG_LTO_CLANG is not set
# CONFIG_CFI_CLANG is not set
这两个宏会导致编译内核时链接阶段的优化,导致工期大幅缩短; 其他解决方案 CodeAurora (CodeLinaro)
众所周知,CodeAurora(现已更名:CodeLinaro)是MediaTek基线的开源部分,因此这里的实现是MediaTek基线的实现,具有一定的权威性;
利润:
与联发科基线一致; 支持各种参数传递;
缺点:
需要移植的文件比较分散,依赖关系不明显,需要仔细整理; 那些与内核一起定义的,以及MediaTek基线的其他分区编译规则,需要被识别和删除; (虽然我们没有相应的源代码下载aosp源码编译,但我们无法编译例如bootloader等产品); 编译不覆盖PATH变量,依赖于当前shell环境下的编译工具链(如clang)。 如果版本较低,则CONFIG_LTO_CLANG=y的内核编译完成; LineageOS
LineageOS本质上是AOSP+设备闭源部分的二补代码文件+内核源码编译,和我们的情况非常相似,具有很高的参考价值;
利润:
移植比较简单,规则稳定成熟;
缺点:
目前不支持kernelmodule的编译(即编译ko文件)自序
至此,源码编译boot.img的目的就达到了。 如果不出意外的话,以后这个问题就不再讨论了;
其实我也尝试参考CodeAurora和LineageOS的实现,以为可以“抄作业”。 但在实际操作过程中,我发现,在不完全理解其编译规则背后的编译逻辑的情况下,需要从别人完整的框架中找出自己需要的东西,并准确地裁剪出来,正确地移植到相应的地方。自有平台的定位并不比从头开始实施更困难;
相反,因为我从头开始操作,除了达到同样的疗效外,还可以加深对编译框架、规则编译等的理解,踩一些坑,积累一些相关经验;
如果读者在操作过程中遇到任何问题,或者有任何疑问下载aosp源码编译,可以添加评论,或者留言讨论遇到的问题,我一听到就会回复;