0%

android r base

信号相关流程梳理

程序分为两种:
1.静态链接: 由链接器在链接时将库的内容添加到可执行程序中的做法,最大缺点是生成的可执行文件太大,需要更多的系统资源,在装入内存也会消耗更多的时间。
2.动态链接: 将独立的模块先编译成动态库,程序运行有需要它们时才加载,最大缺点是可执行程序依赖分别存储的库文件才能正确执行。
在Android中,大部分都是动态链接,而只有init等少部分是静态链接,因此native程序也是动态链接程序,动态链接程序是需要链接器才能跑起来,liner就是Android的链接器。
liner也是在程序的进程空间内,当内核将应用程序加载起来后,并不是先跑应用程序代码,而是先跑liner。linker负责将应用程序所需的库加载到进程空间内,之后才跑应用程序代码。

绝大多数的native app都是以linker为入口(动态连接的程序),在启动时先由汇编跳转到__link_init函数.

[-> arch/arm64/begin.S]

1
2
3
4
5
6
7
8
9
10
ENTRY(_start)
// Force unwinds to end in this function.
.cfi_undefined x30

mov x0, sp
bl __linker_init

/* linker init returns the _entry address in the main image */
br x0
END(_start)

到debuggerd_init的调用链

1
2
3
4
5
6
7
-- bl __linker_init
| - extern "C" ElfW(Addr) __linker_init(void* raw_args) % bionic/linker/linker_main.cpp
\ - __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so)
\ - return start_address = linker_main(args, exe_to_load);
\ - ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load)
\ - linker_debuggerd_init(); % Register the debuggerd signal handler. [ linker_debuggerd_android.cpp ]
\ - debuggerd_init(debuggerd_callbacks_t* callbacks) % [debuggerd/handler/debuggerd_handler.cpp ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void linker_debuggerd_init() {
debuggerd_callbacks_t callbacks = {
.get_abort_message = []() {
return __libc_shared_globals()->abort_msg;
},
.post_dump = &notify_gdb_of_libraries,
.get_gwp_asan_state = []() {
return __libc_shared_globals()->gwp_asan_state;
},
.get_gwp_asan_metadata = []() {
return __libc_shared_globals()->gwp_asan_metadata;
},
};
debuggerd_init(&callbacks);
}

debuggerd_init

阅读全文 »

厂商或者系统继承的交叉工具链中的 gdb 一般不支持 python.
或者即使支持python, 由于路径问题, 很难与当前系统中使用的python 配合. 如

  • 出现python 第三方库不兼容问题等
  • 缺少第三方库, 使用系统下载的第三方库不能嵌入到 gdb 中使用等问题.

因此, 我们在需要定制gdb脚本时往往需要自己动手编译交叉工具链的gdb.

下载编译gdb源码

https://ftp.gnu.org/gnu/gdb/gdb-9.2.tar.xz

1
2
3
4
5
6
7
8
9
10
11
tar gdb-9.2.tar.xz
cd gdb-9.2
mkdir build
../configure --target=mips-mti-elf --prefix=/home/mi/program/mips-gdb \
--enable-unicode=ucs4 \
--with-python=/usr \
--with-auto-load-dir=$debugdir:$datadir/auto-load \
--with-auto-load-safe-path=$debugdir:$datadir/auto-load \
--with-expat --without-libunwind-ia64 --without-lzma --without-babeltrace --without-intel-pt --disable-libmcheck --without-mpfr --without-guile
make
make install

这里注意的地方, target prefix 参数需要自己填

  1. target 这个该怎么填, 可以用这个简单方法, 用系统继承或厂商提供的gdb, 执行下, 在gdb环境下执行show configuration, 看target是什么, 这里就填什么
  2. prefix, 填编译gdb完成后安装到的位置, 添加到环境变量中
  3. with-python 选项是查找 python 的库的位置, 简单来说就是你想定制的 gdb 用的 python 环境是哪个 python 环境, 这里就填哪个目录, ubuntu 等这类 linux 一般会在/usr 下默认配置好 python2, 你如果想使用自己定制的 python, 如使用 pyenvconda``virtualenv 等定制的 python, 则需要填响应的 python 所在的目录. 后面在 gdb 环境下使用 python 报缺少三方库, 则需要用对应环境的 pip 安装对应的库/组件.

使用 gdb 定制 python 脚本

使用上述步骤编译安装的gdb即可, 再进行gdb 环境下定制脚本的开发

阅读全文 »

virtual ab方案调研

从recovery开始说起

Recovery挂载system

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
- CreateSnapshotPartitions
| - SnapshotManager::NewForFirstStageMount "first stage mount"
\ - New(info) "SnapshotManager构造函数,初始化两个目录 gsid_dir_=ota metadata_dir_=/metadata/ota"
| - ForceLocalImageManager "构造ImageManager"
\ - ImageManager::Open(gsid_dir_) "初始化两个目录 metadata_dir = /metadata/gsi/ota data_dir=/data/gsi/ota"
| - sm->NeedSnapshotsInFirstStageMount() "调用上面SnapshotManager的实例的该函数,根据merge state确定是否进行后续步骤"
\ - GetCurrentSlot() != Slot::Target " 根据这个判断是不是ota后的切换slot过程,Target=2,Source=1 "
" =1说明未进行ota,未进行ota就不需要挂载snapshot,return false"
| - ReadUpdateState
\ - ReadSnapshotUpdateStatus " /metadata/ota/state文件中获取ota后的merge状态"
" Unverified Merging MergeFailed 返回true"
| - true - InitRequiredDevices({"userdata"}) "NeedSnapshotsInFirstStageMount 返回true时"
\ - InitDeviceMapper "初始化dm框架,创建uevent监听 /sys/devices/virtual/misc/device-mapper"
| - return UeventCallback(uevent, &devices); "如果下面有userdata设备创建了,才说明required Device初始化成功了,才返回true。 而lp_metadata_partition_是FirstStageMount早期阶段初始化的/devices/virtual/block/dm-<>/dm/name文件中读出的super partition名字,赋值给/dev/block/mapper/<partition>"
| - true - sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_) "创建logical device和snapshot device"
\ - ReadMetadata(opener, super_device, slot) "读取super device对应slot的metadata描述"
| - CreateLogicalPartitionParams (for params.partition:metadata->partitions) "挨个挂接metadata描述的partitions及其对应的snapshot设备"
\ - MapPartitionWithSnapshot(std::move(params))
\ - GetSnapshotStatusFilePath(params.GetPartitionName() "/metadata/ota/snapshots/<partition_name>"
| - ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status) # /metadata/ota/snapshots/<partition_name>,
\ - status->ParseFromFileDescriptor(fd.get()) "解析该文件,获得live_snapshot_status状态"
| - # MERGE_COMPLETED | NONE | "cow_partition_size + cow_file_size = 0 代表no live snapshot"
| - live_snapshot_status.has_value- "如果有live snapshot"
\ - params.device_name = GetBaseDeviceName(params.GetPartitionName()); "devicename变成了<partionname>—base"
| - created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName())
| - CreateLogicalPartition(params, &base_path) "映射logical device 动态分区的映射,返回的path为/dev/block/dm-<num>"
| - live_snapshot_status.has_value- "如果有live snapshot"
\ - dm.GetDeviceString(params.GetDeviceName(), &base_device) "返回base_device major:minor设备号"
| - CreateLogicalPartitionParams cow_params = params
| - MapCowDevices(lock, cow_params, *live_snapshot_status, &created_devices, &cow_name) "映射cow_device写时复制"
\ - cow_image_name = GetCowImageDeviceName(partition_name) #<partion_name>-cow-img
| - *cow_name = GetCowName(partition_name) #<partition_name>-cow
| - EnsureImageManager()
| - "因为前面已经执行过ForceLocalImageManager,所以image_不为空,后面的步骤不走了"
| - MapCowImage(partition_name, remaining_time)
| - has_local_image_manager_- true "image_为之前ForceLocalImageManager时的实例,指向ImageManager"
\ - images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev) "cow_image_name为<partion_name>-cow-img"
| - MapWithDmLinear(opener, cow_image_name, {}, &ignore_path)
\ - metadata = OpenMetadata(metadata_dir_) "metadata_dir_=/metadata/gsi/ota"
\ - metadata_file = GetMetadataFile(metadata_dir_) "/metadata/gsi/ota/lp_metadata"
| | | - metadata = ReadFromImageFile(metadata_file) "从上述文件中解析出metadata描述"
| - super = GetMetadataSuperBlockDevice() "获得super封装"
| - block_device = GetBlockDevicePartitionName(super)
| - CreateLogicalPartition(params, path) "dm-linear 跟之前的动态分区的逻辑一样,映射设备,获得path /dev/block/dm-<num>"
| - WriteStringToFile(status_string, status_file) "/metadata/gsi/ota/<parition_name-cow-img>.status中写入dm:<partition_name-cow-img>"
| - dm.GetDeviceString(name, dev) "返回major:minor号给dev"
| - created_devices->EmplaceBack<AutoUnmapImage>(images_.get(), cow_image_name) "created_devices再push新的cow_img"
| - CreateLogicalPartitionParams cow_partition_params = params (.partition_name=*cow_name;);
"cow_name = <partition>_cow"
| - CreateDmTable(cow_partition_params, &table) "创建映射表"
| - cow_partition_sectors = snapshot_status.cow_partition_size() / kSectorSize "扇区"
| - cow_image_sectors = snapshot_status.cow_file_size() / kSectorSize;
| - table.Emplace<DmTargetLinear>(cow_partition_sectors, cow_image_sectors, cow_image_device, 0);
" 从cow-img的尾部开始算,长度为cow_image_sectors 个扇区,block_device还是用前面挂接的cow-img的device"
| - dm.CreateDevice(*cow_name, table, &cow_path, remaining_time) "为<partition_name>-cow创建dm设备"
| - created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name); "push到created_devices中"
| - dm.GetDeviceString(cow_name, &cow_device) "<partion_name>-cow获取设备号major /minor"
| - MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time, path)
"base_device 代表动态分区的base_device, cow_device代表映射partion_name-cow的设备,为这两个设备之间建立映射快照"
\ - ReadSnapshotStatus(name, &status) "/metadata/ota/snapshots/<partition_name>读取状态,如果是MERGE_COMPLETED|NONE就退出,不需要快照了"
| - snapshot_sectors = status.snapshot_size() / kSectorSize "snapshot快照的扇区数"
| - linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize "logical本身的扇区数"
| - ReadUpdateState() "/metadata/ota/state中读取ota的状态 MergeCompleted| MergeNeedsReboot return false,退出
Merging|MergeFailed mode=merge或者Persistent"
| - snap_name = GetSnapshotExtraDeviceName(partition_name) "<parition_name>-inner"
| - table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode, kSnapshotChunkSize)
"创建映射表,单位是4k,即8*sector_size DmTargetSnapshot start=0. length = snapshot_sectors,mode=merge|Persistent"
"DmTargetSnapshot::name() mode=merge时返回"snapshot-merge",其他状态下返回"snapshot""
| - dm.CreateDevice(snap_name, table, dev_path, timeout_ms) "创建dm设备"
| - linear_sectors>0 - "动态分区里的img不为空 "
\ - dm.GetDeviceString(snap_name, &snap_dev) "获取snap_dev的major/minor"
| - table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0); "先是snap_device"
| - table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device, snapshot_sectors);
"再是linear_device追加"
| - dm.CreateDevice(name, table, dev_path, timeout_ms) "最终创建<partition_name>的dm设备映射"

小结

对于动态分区的挂载流程,最终是按创建设备时的第一个参数device-name进行匹配查找对应的dm设备,从上述映射过程中可以发现,

  • 没有live.snapshot时,只进行了CreateLogicalPartition过程,传入的device_name就是partition_name

  • 而有live snapshot时,第一步的CreateLogicalPartition过程传入的device_name<partition_name>-base,再走完了MapSnapshot过程后,最终创建的dm设备device_name才是partition_name

    映射表,前面是base_device到cow_device的snapshot,后面是其本身的动态分区的数据。

  • 借助动态分区的实现逻辑,既然可以有metadata描述分区信息的描述块这种东西,复用见userdata上cow_device的数据描述放在了metadata分区,然后就可以通过dm-linear 将数据映射成dm设备。
    当前的分析还只是在userspace,跟核心的实现应该是device-mapper的内核实现,dm-snapshot 和dm-snapshot-merge的内部是怎样实现的,才是virtual ab方案的真正的核心。

    snapshot理解

    android的virtual ab方案中snapshot的方案来自于lvm(Logical Volume Manager),要理解snapshot先从lvm说起
    首先可以想通过lvm大致还原snapshot创建的过程

lvm snapshot过程理解

在pc机上创建snapshot过程模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建device的模拟设备
dd if=/dev/zero of=sd10 bs=4M count=1000
# 关联loop设备
losetup /dev/loop0 sd10
# 为loop设备创建lvm的物理分区
sudo pvcreate /dev/loop0
sudo pvscan
# 创建卷组
sudo vgcreate -s 32M nickgroup /dev/loop0
sudo vscan
sudo vgdisplay
# 创建卷
lvcreate -L 2G --name nickgroup/nicklv00
sudo lvscan
# 为卷建立文件系统
sudo mkfs.ext4 /dev/nickgroup/nicklv00
sudo mount -t ext4 /dev/nickgroup/nicklv00 nicklv
echo "hello world" > test_change; echo "i'm not change">test_no_change
# 为卷建立snapshot
sudo lvcreate -L 1G --snapshot --name nicklv00_snap nickgroup/nicklv00
sudo mount -t ext4 /dev/nickgroup/nicklv00_snap nicklv_snap
# 改变原始卷某文件的内容
echo "i'm change" >> nicklv/test_change
阅读全文 »

遇到的问题

  1. 抓kernel log

    fastboot oem uart-enable,重启后,会吐出kernel log, 否则只有kernel之前的log

    提前设置好启动模式,misc不要清除

  2. 关掉avb校验

    fastboot flash vbmeta --disable-verification

    export ANDROID_PRODUCT_OUT=/home/mi/work_space/miui-r-umi-dev/out/target/product/cmi

    make vbmeta

  3. ninja 编译问题

    make编译完后,再使用ninja编译, 会重新完全编译, 打印下面的log。 后来发现是ninja版本的问题,ninja 需要同源码的prebuilts下的相同,就不会重新编译

    bad deps log signature or version; starting over

  4. hidl 出错

    等待进一步调试,manifest是正常的,需要加log调试

    HidlServiceManagement: Waited one second for android.hardware.keymaster@4.0::IKeymasterDevice/default

    stop hwservicemanager后, 自动重启到recovery

    这个是vdc命令触发的,先关掉reboot_into_recovery

    vdc cryptfs mountFstab /dev/block/bootdevice/by-name/userdata /data

    ​ 调试libhidlbase时, 这个so在system分区,但是动态分区的机型recovery模式下只能挂载只读的system。怎样调试是个问题,可以调换ld.config.text文件中lib库的加载顺序,先创建一个虚拟的文件夹,这个文件夹只在recovery启动后启动特别模式后,再装载需要调试的lib库。

    1
    2
    3
    4
    namespace.default.search.paths = /system_lib
    namespace.default.search.paths += /system_root/system/${LIB}
    namespace.default.search.paths += /system_root/system/${LIB}/bootstrap
    namespace.default.search.paths += /system/${LIB}

    不这样处理,如果直接替换recovery的, recovery会无法启动

    从log上看,manifest中定义的keymaster组件是已经找到了, 为什么启动不了呢, 原来是对应的hidl的service的rc没有在mount_all时加载, androidr上修改了mount_all的流程,不再主动加载挂载分区的rc。

    还有一个问题是recovery早期阶段, 创建dm设备时出现device busy问题, 这个还需要进一步分析。

  5. ota适配

    1
    ./ota_from_target_files --no_signing -s ~/work_space/miui-r-umi-dev/vendor/qcom/opensource/recovery-ext/tools/releasetools.py --extracted_input_target_files ~/Downloads/cmi-target_files-0.0.12-11.0_e9b5e7be6c  ~/Downloads/cmi-target_files-0.0.12-11.0_e9b5e7be6c.zip ota_test.zip
  6. ufs分区节点没出来

    1
    2
    3
    4
    5
    #没有dev/block/platform节点, 初步判断原因为ufs_ice没找到 platform节点未生成
    Copying target dtb/dtbo files to prebuilt
    # 与本地dtb编译有关, 将kernel代码删除, 重新sync
    cp: cannot stat '/home/mi/work_space/miui-r-umi-dev/out/target/product/cmi/obj/kernel/msm-4.19/arch/arm64/boot/dts/vendor/qcom/*.dtb': No such file or directory

阅读全文 »

1
2
3
#需要先挂载fastboot
mkdir /config/usb_gadget/g1/functions/ffs.fastboot
mount -t functionfs fastboot /dev/usb-ffs/fastboot

初步调试 清除数据wipe_data进 fastboot, 重启进adb模式

使用wireshark进行调试

1
2
3
4
5
6
7
8
9
10
T:  Bus=01 Lev=01 Prnt=01 Port=08 Cnt=03 Dev#= 40 Spd=480  MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=18d1 ProdID=4ee0 Rev= 4.19
S: Manufacturer=Xiaomi
S: Product=Umi
S: SerialNumber=4e80ec52
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=03 Driver=(none)
E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
1
2
3
4
#打包recovery的ramdisk
out/host/linux-x86/bin/mkbootfs -d out/target/product/umi/system out/target/product/umi/recovery/root | out/host/linux-x86/bin/minigzip > out/target/product/umi/ramdisk-recovery.img
#打包recovery.img
out/host/linux-x86/bin/mkbootfs -d out/target/product/umi/system out/target/product/umi/recovery/root | out/host/linux-x86/bin/minigzip > out/target/product/umi/ramdisk-recovery.img && out/host/linux-x86/bin/mkbootimg --kernel out/target/product/umi/kernel --ramdisk out/target/product/umi/ramdisk-recovery.img --cmdline "console=ttyMSM0,115200n8 androidboot.hardware=qcom androidboot.console=ttyMSM0 androidboot.memcg=1 lpm_levels.sleep_disabled=1 video=vfb:640x400,bpp=32,memsize=3072000 msm_rtb.filter=0x237 service_locator.enable=1 androidboot.usbcontroller=a600000.dwc3 swiotlb=2048 loop.max_part=7 cgroup.memory=nokmem,nosocket reboot=panic_warm androidboot.selinux=permissive androidboot.usbconfigfs=true" --base 0x00000000 --pagesize 4096 --recovery_dtbo out/target/product/umi/prebuilt_dtbo.img --dtb out/target/product/umi/dtb.img --os_version 10 --os_patch_level 2019-09-05 --header_version 2 --output out/target/product/umi/recovery.img

OTA测试环境OTA地址

1
http://husky.pt.miui.com/buildFile/miui_UMI_9.9.20_f2a2d4cc2d_10.0.zip

1. 关于hidl service

1.1. hal简介

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言. 用于进程间通信.分为直通式(passthrough)和绑定式(binderized)

阅读全文 »

AB调试笔记

基于androidP base.

相关文档

官方文档

AB官方文档

在启用了 A/B 的设备上,可以在后台对应用进行编译,以更新到新的系统映像。如需在系统映像中选择性地加入编译脚本和二进制文件,请参阅在后台编译应用。

preopt相关

AB 系统更新概括

AB配置https://source.android.com/devices/tech/ota/ab_implement.html)

第三方文档

阅读全文 »

1. Q动态分区调研

1.1. 相关文档

[Android Bootcamp 2019 - Dynamic Partitions in Q (go_android-dynamic-partitions-slides).pdf](../../../google_document/Android Bootcamp 2019 - Dynamic Partitions in Q (go_android-dynamic-partitions-slides).pdf)

06.Dynamic_Partitions-_LPC_Android_MC_v2.pdf

1.2. 代码路径

bootable/recovery/updater/ dynamic_partitions.cpp

system/core/fs_mgr/ liblp

system/core/fastboot/device fastbootd

build/core/Makefile

1.3. 宏配置

阅读全文 »

log分析

抓串口log方式

打开串口log, 通过串口线获取

使用fastboot oem uart-enable 打开串口log
minicom -c on -C 抓取log.

注意波特率要设置正确, 可以问下相关的底层或适配组的同事. 如果波特率不对, 可能窗口没有任何输出或输出乱码

logfs获取

会保存最近5份开机(包括recovery子系统)的串口log

1
mount /dev/block/bootdevice/by-name/logfs /data/log

获取kernel log

高通sm8250平台可以通过电量下+电源键(长按3s)进入ramdump模式
进入后, 连接qpst工具抓取即可. kernel log会优先输出

阅读全文 »

优化cache

cache文件系统损坏问题

因文件系统损坏, 可能导致可用空间信息不准确, 或cache空间变成一半.
开机加入强制check cache, 可以解决可用空间错误的问题.

http://gerritlvs.pt.miui.com/#/c/641165/

原生方案

没有强制check cache. 开机阶段对cache分区进行检查, 如果出现过异常关机的情况, 会对cache分区进行修复.

  • fstab中options配置了check
  • 读取文件系统超级块信息时返回的状态是FS_STAT_UNCLEAN_SHUTDOWNFS_STAT_QUOTA_ENABLED

FS_STAT_UNCLEAN_SHUTDOWN状态可以理解完出现了异常关机

FS_STAT_QUOTA_ENABLED状态则是分区是否启用了磁盘配额机制

1
2
3
4
5
6
read_ext4_superblock(blk_device, &sb, &fs_stat)
// fstab中options配置了check或者读取文件系统超级块信息时返回的状态是FS_STAT_UNCLEAN_SHUTDOWN或FS_STAT_QUOTA_ENABLED
if ((rec->fs_mgr_flags & MF_CHECK) ||
(fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat, rec);
}
因cache文件系统损坏导致的recovery无法更新成功问题
阅读全文 »

设备bootloader上锁状态下1217无法工作问题分析

背景

前面讲到r上6月份升级基线后, 因KM TA date support的升级导致recovery下无法正常使用解密服务.

当时的方案是配置recovery vbmeta区域的 prop --prop com.android.build.boot.security_patch:2020-06-05

从而在bootimg_hdr不支持识别date 信息的情况下, 可以在reocovery模式下bootloader能够读到天的信息, 并传给KM TA.

新问题

最近测试在复测该问题时, 发现手机bootloader上锁状态下, 无法执行1217功能. 因此需要重新分析下这个问题的原因.

locked 状态下的调试

debug abl的方法

locked状态下的限制

阅读全文 »