一个boot.oat crash问题的分析

2023-11-20

最近遇到一个手机重启的问题,日志如下:

05-18 13:42:55.553 I/AEE/AED (10514): pid: 1734, tid: 1788, name: android.ui  >>> system_server <<<
05-18 13:42:55.554 I/AEE/AED (10514): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
05-18 13:42:55.636 I/AEE/AED (10514):     x0   0000000070e4c750  x1   0000007f90aa4200  x2   0000000000000000  x3   aba39cecf7cb9912
05-18 13:42:55.636 I/AEE/AED (10514):     x4   aba39cecf7cb9912  x5   aba39cecf7cb9912  x6   0000007f9b1ba000  x7   0000007f9ee79888
05-18 13:42:55.636 I/AEE/AED (10514):     x8   aba39cecf7cb9912  x9   aba39cecf7cb9912  x10  aba39cecf7cb9912  x11  0000007f90aa4200
05-18 13:42:55.636 I/AEE/AED (10514):     x12  0000007f84f7ee40  x13  0000000000000000  x14  0000000000000030  x15  0000000000000000
05-18 13:42:55.637 I/AEE/AED (10514):     x16  00000000018c54dd  x17  0000000000000003  x18  0000007f90aa4200  x19  0000000000000001
05-18 13:42:55.637 I/AEE/AED (10514):     x20  0000000012d4a070  x21  0000000000000000  x22  0000000012d4a070  x23  0000000070e48e00
05-18 13:42:55.637 I/AEE/AED (10514):     x24  0000000000000000  x25  0000000000000000  x26  0000007f8566a8d0  x27  00000000ffffffff
05-18 13:42:55.637 I/AEE/AED (10514):     x28  0000000000157e92  x29  0000007f84f7f118  x30  000000007492bcd0
05-18 13:42:55.637 I/AEE/AED (10514):     sp   0000007f84f7eee0  pc   0000000000000000  pstate 0000000060000000
05-18 13:42:55.638 E/AEE/LIBAEE(10514): aee_try_get_word: read:1788 addr:0x0000000000000000 ret:-1, 5
05-18 13:42:55.641 I/AEE/AED (10514): 
05-18 13:42:55.641 I/AEE/AED (10514): backtrace:
05-18 13:42:55.641 I/AEE/AED (10514):     #00 pc 0000000000000000  <unknown>
05-18 13:42:55.642 I/AEE/AED (10514):     #01 pc 00000000039f9ccc  /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x257c000)
堆栈十分的短,只有两行。

Android4.4之后引入了art虚拟机,会预先把system/framework中的一些jar包转换为oat格式,这个格式实际上是elf可执行文件格式的变种。这个转换的作用是把虚拟机的字节码直接转换成cpu的机器码,这样不用在java虚拟机运行的时候解释字节码,会提高程序运行速度。

但是其实本质上还是java代码,如果是java层次的出错会抛出Exception,分析起来十分容易,如果是调用到so库出错,那么就是native crash,网上分析的文章也很多。日志这个问题比较少出现,它既不是java层面的Exception,也没有调用到任何so库,但是看起来和native crash的日志十分相像。

dump oat文件信息的程序是oatdump,例如连接上手机。

adb shell oatdump --oat-file=/data/dalvik-cache/arm64/system@framework@boot.oat

或者在pc本地运行oatdump,不用连接手机。但是直接用这个命令dump的文件会非常大,我本地看有1G的大小,而且大部分的信息对于crash来说完全是无用的,幸好oatdump有如下参数

  --addr2instr=<address>: output matching method disassembled code from relative  
                          address (e.g. PC from crash dump)  
      Example: --addr2instr=0x00001a3b  
指定地址即可只dump地址附近的信息,那么这个address是什么,如何确定,是00000000039f9ccc,还是0x257c000,亦或是其它

05-18 13:42:55.642 I/AEE/AED (10514):     #01 pc 00000000039f9ccc  /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x257c000)

估计大多数android程序员例如我面对此种问题是懵逼的感觉,幸好有万能的搜索引擎。可是网上搜索分析oat crash的可真是少,我搜索到两个网页和这个问题相关。

oat backtrace 地址 定位?


还有oat文件的 backtrace 定位


可以看出这两个文章间都有差异,知乎回答和评论间也有差异(而且回答和评论里的地址计算个人认为都是错的,作者明显是懂这个问题,但是回答太随意了)。不过感谢这两篇文章给了我分析此问题的起点。

文章中提到的maps是,/proc/(出错进程pid)/maps,这个可以看内存布局,例如在手机(烧录出问题的rom版本)我查看如下

6f653000-70520000 rw-p 00000000 fd:00 32776                              /data/dalvik-cache/arm64/system@framework@boot.art

70520000-72a9c000 r--p 00000000 fd:00 32774                              /data/dalvik-cache/arm64/system@framework@boot.oat

72a9c000-75321000 r-xp 0257c000 fd:00 32774                              /data/dalvik-cache/arm64/system@framework@boot.oat

75321000-75322000 rw-p 04e01000 fd:00 32774                              /data/dalvik-cache/arm64/system@framework@boot.oat

这里看出日志中的0x257c000是指代码段对于整个oat文件的偏移,那么oat在内存中的偏移就是72a9c000。日志中的pc是00000000039f9ccc,按搜索文章所说要减去72a9c000的话明显感觉不对,这个个人推测应该是google或者mtk的优化,pc地址已经减去了文件基址,因为只有拿到手机才能看到maps,日志中大概率是看不到maps相关信息的,而且maps地址对于查看堆栈其实没啥意义,获取代码相对于文件的偏移才是关键。

然后兴高采烈的把0x039f9ccc作为参数去oatdump,然后又懵逼了,输出结果不对,一个汇编代码都没有。想起来搜索文章中提到过日志偏移和dump偏移有差距0x1000,查看dump文件,真的和文章中一样,dump的比日志中的0x257c000小了0x1000;

EXECUTABLE OFFSET:

0x0257b000
虽然不明白具体原理,还是把参数减少0x1000,继续尝试,但是还是同样的结果,懵逼了。oatdump整个文件看代码的地址到不了0x039f9ccc,所以当然没啥输出了。仔细查看dump文件发现了问题:

SEARCH ADDRESS (executable offset + input):

0x04af7000

addr2instr参数传入的address是会自动加上executable offset,这个估计还是优化,那么addr2instr参数传入的值应该是:代码相对于代码段的offset,不用考虑文件在内存中的偏移和代码段相对于文件的偏移,即把日志中的pc减去offset传入即可,即00000000039f9ccc -  0x257c000就是参数。获取的汇编代码片段如下,出问题的就是0x039f8ccc,即blr

      suspend point dex PC: 0x0018

      GC map objects:  v0 (r22), v18 (r20)

      0x039f8cbc: d5033bbf	dmb ish

      0x039f8cc0: d0fdc3a0	adrp x0, #-0x478a000 (addr -0xd92000)

      0x039f8cc4: f9446c00	ldr x0, [x0, #2264]

      0x039f8cc8: f940181e	ldr lr, [x0, #48]

      0x039f8ccc: d63f03c0	blr lr

      suspend point dex PC: 0x0019

继续百度arm blr指令,发现blr是直接可以控制pc的少数指令之一(好像只有两个),那么和日志中正好有某种对应:

05-18 13:42:55.641 I/AEE/AED (10514):     #00 pc 0000000000000000  <unknown>
05-18 13:42:55.642 I/AEE/AED (10514):     #01 pc 00000000039f9ccc  /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x257c000)

日志中出错点pc就是突然变为了0,到此问题算是有了一定的进展。可是汇编代码看起来还是十分头疼的,能看对应的Java代码就开心了,oatdump的文件中的对应方法是

  24: android.os.Message android.os.MessageQueue.next() (dex_method_idx=45283)
虽然不能直观的确定行数,不过对照suspend point dex PC大概可以确认在0x0018和0x0019之间,对比oatdump对应的dex代码

      0x0015: 7040 e0b0 c06d           	| invoke-direct {v0, v12, v13, v6}, void android.os.MessageQueue.nativePollOnce(long, int) // method@45280

      0x0018: 1d12                     	| monitor-enter v18

      0x0019: 7100 69b4 0000           	| invoke-static {}, long android.os.SystemClock.uptimeMillis() // method@46185
对应的Java代码

 Message next() {
        ...
        for (;;) {
            ...
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                ...
            }

            ...
        }
    }
是从synchronized开始到final long now = SystemClock.uptimeMillis();

问题到此为止没有继续分析了,原因一:真正要分析要对arm汇编和操作系统等有深厚功底的,现学现卖搞不定,对一个写app的未免有点难。原因二:这个问题只出现过一次,就算功底深厚现场也很难复现了,依据网上信息是一步步用gdb查看赋给pc的内存数据为啥会是0,然后推测原因。

本文纯属个人推测,希望有能力的同学遇到此种问题的时候能交流一下。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个boot.oat crash问题的分析 的相关文章

随机推荐

  • 《塞尔达传说:旷野之息》中设计元素的分析

    塞尔达传说 旷野之息 中设计元素的分析 0 写在前面 关于 塞尔达传说 旷野之息 是否属于中型游戏 检索许多资料后 有一种通识是 塞尔达传说 旷野之息 不属于3A级别游戏 显然也不属于小型游戏 因此我姑且认为它属于中型游戏 这也符合此篇的初
  • crypto-js md5加密和解密

    直接上代码 import CryptoJS from crypto js const encodeFactor zq87dopenf67eg 加密 export function encrypt txt var key CryptoJS e
  • 服务攻防-中间件安全&CVE复现&IIS&Apache&Tomcat&Nginx漏洞复现

    目录 一 导图 二 ISS漏洞 中间件介绍 gt 1 短文件 2 文件解析 3 HTTP SYS 4 cve 2017 7269 三 Nignx漏洞 中间件介绍 gt 1 后缀解析漏洞 2 cve 2013 4547 3 cve 2021
  • openstack平台搭建笔记(容器云)

    openstack平台搭建笔记 容器云 一 根据要求准备好配置环境 节点IP 角色 备注 192 168 100 30 Master Kubernetes 集群 master 节点 Harbor 仓库节点 192 168 100 31 Wo
  • C# 快速写入日志 不卡线程 生产者 消费者模式

    有这样一种场景需求 就是某个方法 对耗时要求很高 但是又要记录日志到数据库便于分析 由于访问数据库基本都要几十毫秒 可在方法里写入BlockingCollection 由另外的线程写入数据库 可以看到 在我的机子上面 1ms写入了43条日志
  • html5 自动化测试工具,五大最佳自动化测试工具

    对更快交付高质量软件 或 快速质量 的需求要求组织以敏捷 持续集成 CI 和DevOps方法论来寻找解决方案 测试自动化是这些方面的重要组成部分 最新的 2018 2019年世界质量报告 表明 测试自动化是实现 快速质量 的最大瓶颈 因为它
  • 四位数显表头设计

    去年帮别人定制了一个四位数显小表头 可以用于测量4 20mA或者0 5V 0 10V输出的的各种传感器 可设置显示范围 上下限报警灯 由于后面更改方案 此方案暂时搁置不用 今天来分享一下软硬件的设计过程 1 硬件设计 1 1电源 电源采用一
  • Flink_06_ProcessAPI(个人总结)

    声明 1 本文为我的个人复习总结 并非那种从零基础开始普及知识 内容详细全面 言辞官方的文章 2 由于是个人总结 所以用最精简的话语来写文章 3 若有错误不当之处 请指出 侧输出流 SideOutput 即分支流 可以用来接收迟到数据 也可
  • SpringBoot实现接口版本控制

    一个系统在上线后会不断迭代更新 需求也会不断变化 有可能接口的参数也会发生变化 如果在原有的参数上直接修改 可能会影响到现有项目的正常运行 这时我们就需要设置不同的版本 这样即使参数发生变化 由于老版本没有变化 因此不会影响上线系统的运行
  • python的UnboundLocalError: local variable 'xxx' referenced before assignment

    From http blog sina com cn s blog 8d3652760101d01p html 一 意思 本地变量xxx引用前没定义 二 错误原因 在于python没有变量的声明 所以它通过一个简单的规则找出变量的范围 如果
  • OPENV接收和发送串口的数据

    import sensor image time from pyb import UART from pyb import Pin Timer LED import re sensor reset sensor set pixformat
  • qt 开发遇到的坑

    1 QString的toString 和toWString 引起的win32位release 下std string的析构崩溃 代码 QString qs std string str qs toStdString const wchar
  • Linux NFS说明,配置及故障分析

    一 NFS服务简介 NFS 是Network File System的缩写 即网络文件系统 一种使用于分散式文件系统的协定 由Sun公司开发 于1984年向外公布 功能是通过网络让不同的机器 不同的操作系统能够彼此分享个别的数据 让应用程序
  • MATLAB:figure的用法

    figure的定义 figure 创建图窗窗口 可以理解为创建一个有画板的窗口 我们在这块画板上绘制 plot 曲线等 figure主要是创建图窗窗口或者切换图窗窗口 figure n 查找到n存在时 将当前窗口切换成n 不存在时创建标识为
  • Java的String类、Object类、包装类

    1 String类 1 1 String类的两种实例化方式 1 直接赋值 String str hello 2 使用构造方法new的形式赋值 String str new String hello 1 2 String类定义的字符串的比较
  • Eclipse安装SVN插件

    http subclipse tigris org servlets ProjectProcess jsessionid A870EAC9A292637E167F9719F6399F60 pageID p4wYuA Installation
  • Binary Tree on Plane【费用流】

    题目链接 CF 277 E 题意翻译 给你平面上 n 个点 2 n 400 要求用这些点组成一个二叉树 每个节点的儿子节点不超过两个 定义每条边的权值为两个点之间的欧几里得距离 求一个权值和最小的二叉树 并输出这个权值 其中 点 i 可以成
  • Qt导出库接口类无法connect信号

    问题 动态库中的接口类内部有信号 但是在主程序中无法connect 链接时报错 undefined symbol xxx staticMetaObject 解决 在接口类前加导出标记 参考动态库的隐式调用 一般接口类如果不需要继承实现的话
  • 2013年9月22日---2013年10月5日(每天1小时,共15小时,还有5050小时)

    之所以写每天1小时 是因为这段时间浮躁了 面临培训 过于兴奋 另外一个是假期闹得 不过 每天1小时肯定有 为了这1万小时的计算不浮夸 宁可少一些
  • 一个boot.oat crash问题的分析

    最近遇到一个手机重启的问题 日志如下 05 18 13 42 55 553 I AEE AED 10514 pid 1734 tid 1788 name android ui gt gt gt system server lt lt lt