一个boot.oat crash问题的分析

本文针对Android设备上出现的系统重启问题进行深入分析,通过解读ART虚拟机产生的crash日志,详细阐述了如何利用oatdump工具定位故障原因,并最终追溯到MessageQueue组件中的异常行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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,然后推测原因。

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


Timestamp: 2025-07-11 18:03:09.919649011+0800 Process uptime: 1s Cmdline: com.playit.videoplayer pid: 14201, tid: 14201, name: yit.videoplayer >>> com.playit.videoplayer <<< uid: 10340 tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE) signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x000000000000000c Cause: null pointer dereference x0 00000000711d8138 x1 00000000700412d0 x2 0000000000000000 x3 000000000000000c x4 0000000000000000 x5 b4000077d022efb7 x6 0000000000000012 x7 00000078230447f6 x8 0000007823040309 x9 000000782304030e x10 0000000000000004 x11 0000000000000000 x12 0000000000001a72 x13 00000078ab5433d8 x14 b40000782b06dc00 x15 0000000000000030 x16 0000000000000000 x17 0000000000000000 x18 00000078ff4a8000 x19 b40000782b06dc00 x20 0000000000000000 x21 b40000782b06dcc0 x22 00000077eb9484aa x23 000000782304030a x24 0000007823985a00 x25 0000007fe4d8efe0 x26 0000000010380009 x27 0000000000000034 x28 0000007fe4d8f050 x29 0000007fe4d8f018 lr 000000782398fb04 sp 0000007fe4d8efa0 pc 0000000072a678b4 pst 0000000080001000 28 total frames backtrace: #00 pc 00000000012d78b4 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (art_jni_trampoline+4) #01 pc 000000000068ab00 /apex/com.android.art/lib64/libart.so (nterp_helper+5648) (BuildId: d57befa204d91d200485ace46c3b8814) #02 pc 000000000015a4aa /data/app/~~pA1TasqVPuaPXRL-wamgcA==/com.playit.videoplayer-u-_dKoL3drpKDtv_vnOQlg==/base.apk (org.lsposed.hiddenapibypass.h.a+182) #03 pc 0000000000689588 /apex/com.android.art/lib64/libart.so (nterp_helper+152) (BuildId: d57befa204d91d200485ace46c3b8814) #04 pc 000000000015a310 /data/app/~~pA1TasqVPuaPXRL-wamgcA==/com.playit.videoplayer-u-_dKoL3drpKDtv_vnOQlg==/base.apk (org.lsposed.hiddenapibypass.h.b+4) #05 pc 0000000000689588 /apex/com.android.art/lib64/libart.so (nterp_helper+152) (BuildId: d57befa204d91d200485ace46c3b8814) #06 pc 000000000026915e /data/app/~~pA1TasqVPuaPXRL-wamgcA==/com.playit.videoplayer-u-_dKoL3drpKDtv_vnOQlg==/base.apk (com.quantum.player.common.QuantumApplication.attachBaseContext+154只)根据当前这个堆栈信息分析出该进程14201是为什么发生crash,安装包的问题吗,需要怎么解决呢
最新发布
07-24
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值