AOSP编译保姆级教程(一)
今天记录一下AOSP编译过程,主要包含准备和增加部分功能的描述,那么don't say too much😉,直接开搞
准备
系统环境
系统版本:Ubuntu 20.04
Java版本:使用源码自带的JDK,路径$AOSP_HOME/$prebuilts/jdk
Python版本:2.7.18、3.8.10
安装repo
$ mkdir ~/bin
$ PATH=~/bin:$PATH
$ curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
$ chmod a+x ~/bin/repo
初始化 Repo 客户端
$ git config --global user.name "Your Name"
$ git config --global user.email "you@example.com"
# 指定同步的版本
同步源码
AOSP | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror
# init时指定只克隆包含最近一次commit的一个分支,可减少同步的代码数量,减少项目文件大小
$ repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-9.0.0_r46 --depth=1
# 同步代码,开始用的repo sync,但是缺少大量文件,后面用了repo sync -l文件才被checkout,具体原因未知
$ repo sync
$ repo sync -l
创建本地分支
# 创建分支
repo start YOURBRANCH --all
下载驱动
下载后的驱动放在源码文件夹中执行,执行完成后源码根目录下会生成vendor文件夹,其中存在如下两个文件夹
[图片上传失败...(image-cf7360-1676707996043)]
查看系统版本和代号等信息
代号、标签和内部版本号 | Android 开源项目 | Android Open Source Project
下载对应版本二进制文件
Driver Binaries for Nexus and Pixel Devices | Google Play services | Google Developers
关闭预优化加快编译速度
- 打开build/core/main.mk
- 搜索eng
- 在判断处增加
WITH_DEXPREOPT:=false
禁用Jackserver
设置ANDROID_COMPILE_WITH_JACK:=false
make 'ANDROID_COMPILE_WITH_JACK:=false' -j1
添加frida-gadget.so
Zygote中添加
- 将fridagadget.so和fridagadget.config.so存放到out/target/product/sailfish/system/lib64目录下
- 修改aosp/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp,的ForkAndSpecializeCommon函数中添加对应功能,在App启动前将so注入
- 在ForkAndSpecializeCommon的if (pid == 0) 分支中增加以下内容
//此处通过修改buildinfo.sh增加对应属性来设置需要注入的App的包名,也可通过其他方式实现,如修改fridagadget.config.so的配置
std::string package = android::base::GetProperty("fd.packageName","");
//此处通过修改buildinfo.sh增加对应属性来设置第一步的fridagadget.so路径使其默认打开对应fridagadget.so
std::string gdpath = android::base::GetProperty("fd.gdpath64","");
//或者std::string gdpath{ "/system/lib64/gdpath.so" };
std::string gdpath32 = { "/system/lib64/gdpath32.so" };
if(package.compare(se_name_c_str) == 0){
void* gadget64 = dlopen(gdpath.c_str(),RTLD_NOW);
if(NULL == gadget64){
ALOGE("load gadget64 for app %s failed",se_name_c_str);
}else{
ALOGE("load gadget64 for app %s success",se_name_c_str);
}
}
delete se_info;//此处为原始代码,当前编译的aosp增加gadget的位置即在此处以上
ActivityThread中添加(来自MikRom的方案)
- 将fridagadget.so和fridagadget.config.so存放到out/target/product/sailfish/system/lib64目录下
- 在App启动前将so注入
- 修改android.app.ActivityThread的handleBindApplication方法,增加对应功能,在App启动前将so注入
int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags;
if(flags>0&&((flags&ApplicationInfo.FLAG_SYSTEM)!=1)){
Fartext.loadGadget();
}
app = data.info.makeApplication(data.restrictedBackupMode, null); //在此段代码前添加
MikRom做了很多判断,主要是写入配置文件,判断系统类型等,如果只需要将gadget注入,则主要代码就是下面这些,感兴趣的去可以看下MIKROM源码
public static void loadSo(String path){
String processName = ActivityThread.currentProcessName();
String fName = path.trim();
String fileName = fName.substring(fName.lastIndexOf("/")+1);
String tagPath = "/data/data/" + processName + "/"+fileName;//64位so的目录
mycopy(path, tagPath);
int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;
FileUtils.setPermissions(tagPath, perm, -1, -1);//将权限改为777
File file = new File(tagPath);
if (file.exists()){
Log.e("mikrom", "load so src:"+path+" to:"+tagPath);
System.load(tagPath);
file.delete();//用完就删否则不会更新
}
}
fridagadget.config.so配置
{
"interaction": {
"type": "listen", //模式,有listen、Connect、script、ScriptDirectory四种
"address": "0.0.0.0", //侦听地址,支持IPV4和IPV6
"port": 27042, //侦听端口
"on_port_conflict": "fail", //两种模式,fail和pick-next,fail:端口被占用则不启动,pick-next:继续尝试下一个端口直到端口可用
"on_load": "wait" //两种模式resume和wait,wait:会等待附加,resume:不会
}
}
增加可修改的properties
大家可以看到,通过zygote增加gadget时使用了GetProperty来获取gadget的path和需要注入的App的packageName,但是如果编译user版本是无法自定义properties的,此时可以通过修改build/make/tools/buildinfo.sh和system/core/init/property_service.cppproperty_service.cpp来增加
# buildinfo.sh
echo "fd.gdpath64=/system/lib64/gdpath.so"
// property_service.cpp
// 在CheckMacPerms方法中返回前增加如下内容
if (StartsWith(name, "fd.")) { return true; }
此时即可实现自定义以fd开头的properties,如果需要其他的方案可根据需求修改。
编译
开始编译
#激活环境
source ./build/envsetup.sh
#设置编译版本
lunch
#或者
lunch aosp_sailfish-user
# 设置编译线程
make -j2
其他编译相关命令
# 部分编译
mmm frameworks/base # 编译指定目录且不编译依赖
mm #编译当前路径下的模块且不编译依赖
mmma frameworks/base # 编译指定目录且编译依赖
# 修改隐藏API:@hide,必须先执行以下命令
make update-api
# 将部分编译后的模块打包进img
make snod
其他记录
Repo常用命令
repo相关命令 - 张大猛 - 博客园 (cnblogs.com)
# 创建分支
repo start YOURBRANCH --all
# 遍历并执行git命令
repo forall -c git xxx
# 切换分支
repo forall -c git checkout default
# 丢弃分支
repo forall -c git git reset --hard HEAD
# 查看每个目录下的分支并且打印目录路径
repo forall -p -c git branch # -p参数在遍历到每个仓库的时候先打印出当前的路径再执行git命令
# 查看分支
repo branch
# add
# commit...
问题记录
-
编译失败
ninja: build stopped: subcommand failed. 14:52:38 ninja failed with: exit status 1
解决方案1:尝试:在~/.bashrc中增加 export LC_ALL=C
解决方案2:查看是否使用的为JDK8
解决方案3:看看是否内存不足,如果内存不足则减少编译线程或增加内容和swap
sudo dd if=/dev/zero of=/swapfile bs=1G count=12 sudo mkswap -f /swapfile sudo swapon /swapfile sudo swapoff /swap rm /swap sudo vim /etc/fstab #将里面的swap文件名改成新的swapfile
-
模拟器启动失败
# 出现: context mismatch in svga_surface_destroy # 执行 export SVGA_VGPU10=0
-
如果是Ubuntu 20.04.2 LTS全新安装,则可能没有Python2,此时会报错
/usr/bin/env 'python' no such file or directory
,解决方法如下(但是,编译旧版本时还是需要安装python2的,因为部分编译脚本为Python2,目前编译9.0版本时需要安装Python2):sudo ln -s /usr/bin/python3 /usr/bin/python