一、前言,发现新玩意
最近Unity
项目打出的Android
包在红米Note4
真机上运行闪退了,查看日志如下:
07-28 17:56:49.623 7368 7368 D YSDK d.OnSupport: Device OAID loadSuccesstrue
07-28 17:56:49.623 7368 7416 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x1 in tid 7416 (YSDK_TEMP_THREA)
07-28 17:56:49.624 7368 7368 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x1 in tid 7368 (mgp.linxinfagame)
07-28 17:56:49.624 7368 7368 I libc : Another thread contacted debuggerd first; not contacting debuggerd.
07-28 17:56:49.646 7368 7418 I MID : >>> queryMatchContentProviders size:1
07-28 17:56:49.647 7368 7418 D MID : >>> appPrivateMidMap size:0,content:
07-28 17:56:49.651 7368 7397 I XgStat : [tpush.working.thread(590): null:-1] - read mid from sharedPreferences, key=__MTA_DEVICE_INFO__1000001
07-28 17:56:49.652 7368 7397 I XgStat : [tpush.working.thread(590): null:-1] - read mid from sharedPreferences, key=__MTA_DEVICE_INFO__3
07-28 17:56:49.677 637 637 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
07-28 17:56:49.677 637 637 F DEBUG : Build fingerprint: 'Xiaomi/prada/prada:6.0.1/MMB29M/V10.2.2.0.MCECNXM:user/release-keys'
07-28 17:56:49.677 637 637 F DEBUG : Revision: '0'
07-28 17:56:49.677 637 637 F DEBUG : ABI: 'arm'
07-28 17:56:49.677 637 637 F DEBUG : pid: 7368, tid: 7416, name: YSDK_TEMP_THREA >>> com.tencent.tmgp.linxinfagame <<<
07-28 17:56:49.677 637 637 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x1
07-28 17:56:49.688 3771 3771 E klogd : 6>[45073.921305] type=1400 audit(1595930209.671:1826): avc: denied { search } for pid=637 comm="debuggerd" name="com.tencent.tmgp.linxinfagame" dev="dm-1" ino=81623 scontext=u:r:debuggerd:s0 tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=dir permissive=0
07-28 17:56:49.692 637 637 F DEBUG : r0 710f98f8 r1 00000001 r2 ddf6feb4 r3 00000000
07-28 17:56:49.692 637 637 F DEBUG : r4 70a40770 r5 130c27a0 r6 00000001 r7 130c29f0
07-28 17:56:49.693 637 637 F DEBUG : r8 12c5bb00 r9 ab5de3b0 sl 12c7fb00 fp ddf7010c
07-28 17:56:49.693 637 637 F DEBUG : ip 0000f682 sp ddf6fff0 lr e1565349 pc 7479d05c cpsr 000f0030
07-28 17:56:49.693 637 637 F DEBUG :
07-28 17:56:49.693 637 637 F DEBUG : backtrace:
07-28 17:56:49.693 637 637 F DEBUG : #00 pc 7479d05c /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x256a000)
07-28 17:56:49.694 637 637 F DEBUG : #01 pc 01211347 /data/app/com.tencent.tmgp.linxinfagame-1/oat/arm/base.odex (offset 0x9fd000)
07-28 17:56:49.695 3771 3771 E klogd : 6>[45073.937028] type=1400 audit(1595930209.681:1827): avc: denied { search } for pid=637 comm="debuggerd"
name="com.tencent.tmgp.linxinfagame" dev="dm-1" ino=81623 scontext=u:r:debuggerd:s0 tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=dir permissive=0
07-28 17:56:49.901 1931 1931 W ResourceType: No package identifier when getting name for resource number 0x00000000
07-28 17:56:49.979 3005 3005 D wpa_supplicant: wlan0: Control interface command 'SIGNAL_POLL'
07-28 17:56:49.994 3005 3005 D wpa_supplicant: CTRL-DEBUG: global_ctrl_sock-sendto: sock=13 sndbuf=229376 outq=0 send_len=48
07-28 17:56:50.320 1512 7424 I am_crash: [1512,0,com.tencent.tmgp.linxinfagame,982040132,Native crash,Segmentation fault,unknown,0]
可以看到触发了一个致命信号SIGSEGV
,最终crash
闪退了。
日志里输出了闪退时的调用堆栈(backtrace
):
07-28 17:56:49.693 637 637 F DEBUG : backtrace:
07-28 17:56:49.693 637 637 F DEBUG : #00 pc 7479d05c /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x256a000)
07-28 17:56:49.694 637 637 F DEBUG : #01 pc 01211347 /data/app/com.tencent.tmgp.linxinfagame-1/oat/arm/base.odex (offset 0x9fd000)
这里出现了两个奇怪的文件:.oat
和.odex
,我们知道,Unity
打出的Android
包(.apk
),里面是有一个classes.dex
文件的,如下
那么,.dex
与.odex
、.oat
有什么联系呢?
二、.dex、.odex与.oat文件介绍
1、dex文件
在我们写Java
代码的时候,生成的文件是.java
文件。
对于PC
上的java
虚拟机(JVM
)运行的是.class
。
.java
文件转成.class
文件,需要jdk
工具,转换命令:
javac xxxx.java
关于JavaEE和JavaSE
JavaEE:Java Enterprise Edition,Java企业版,多用于企业级开发,包括web开发等等。企业版本帮助开发和部署可移植、健壮、可伸缩切安全的服务端Java应用。Java EE是在JavaSE的基础上构建的他提供Web 服务、组建模型、管理和通信API.可以用来实现企业级的面向服务体系结构(service-oriented architecture,SOA)和web2.0应用程序。
JavaSE:通常是指Java Standard Edition,Java标准版,就是一般Java程序的开发就可以(如桌面程序),可以看作是JavaEE的子集。它允许开发和部署在桌面、服务器、嵌入式环境和实施环境中使用的Java应用程序。JavaSE 包括支持Java Web服务开发的类,并为Java Platform,Enterprise Edition(Java EE)提供基础。
关于JVM虚拟机
为了使代码和平台无关,JAVA开发了 JVM,即 Java 虚拟机。它为每一个平台开发一个 JVM,也就意味着 JVM 是和平台相关的。Java 编译器将 .java 文件转换成 .class文件,也就是字节码。最终将字节码提供给 JVM,由 JVM 将它转换成机器码。
在Android
端,Android
上的Davlik
虚拟机是运行.dex
。所以还得将.class
转成dex
文件,即dex
文件就是Android Dalvik
虚拟机运行的程序。
.class
转成dex
文件 需要使用dx.bat
工具,dx.bat
工具在Android SDK
中build-tools
目录中可以找到,转换命令:
dx --dex --output = C:\output.dex C:\test
其中C:\output.dex
表示输出文件,C:\test
表示原文件的路径名。
关于Dalvik虚拟机
Dalvik是Google公司自己设计用于Android平台的虚拟机,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
很长时间以来,Dalvik虚拟机一直被用户指责为拖慢安卓系统运行速度不如IOS的根源。
2014年6月25日,Android L 正式亮相于召开的谷歌I/O大会,Android L 改动幅度较大,谷歌将直接删除Dalvik,代替它的是传闻已久的ART。
关于ART
Dalvik 使用 JIT(Just in time)编译,而 ART 使用 AOT(Ahead of time)编译。Android 7.0 向 ART 中添加了一个 just-in-time(JIT)编译器,这样就可以在应用运行时持续的提高其性能。
ART 和 Dalvik 一样使用的是相同的 DEX 字节码。编译好的应用如果使用 ART 在安装时需要额外的时间用于编译,同时还需要更多的空间用于存储编译后的代码。
由于 ART 直接运行的是应用的机器码(native execution),它所占用的 CPU 资源要少于 使用 JIT 编译的 Dalvik。由于占用较少的 CPU 资源也就消耗更少的电池资源。
关于JIT (Just In Time )
使用 Dalvik JIT 编译器,每次应用在运行时,它实时的将一部分 Dalvik 字节码翻译成机器码。在程序的执行过程中,更多的代码被被编译并缓存。由于 JIT 只翻译一部分代码,它消耗的更少的内存,占用的更少的物理存储空间。
关于AOT(Ahead Of Time)
ART 内置了一个 Ahead-of-Time 编译器。在应用的安装期间,他就将 DEX 字节码翻译成机器码并存储在设备的存储器上。这个过程只在将应用安装到设备上时发生。由于不再需要 JIT 编译,代码的执行速度要快得多。
常规的反编译dex流程:
1、拿到apk文件,然后解压 ,得到 class.dex 文件
2、用dex2jar 把 class.dex 还原成 classes-dex2jar.jar 文件
3、用 jd-gui.exe 把 classes-dex2jar.jar 文件打开,就可以看到源码了。
2、vdex文件
在讲odex
之前,需要先讲vdex
(Android O
开始加入的)
package
直接转化的 可执行二进制码 文件:
1.第一次开机就会生成在/system/app/<packagename>/oat/
下;
2.在系统运行过程中,虚拟机将其 从“/system/app”
下 copy
到“/data/davilk-cache/”
下
为何要搞出个vdex文件
目的不是为了提升性能,而是为了避免不必要的验证Dex 文件合法性的过程,例如首次安装时进行dex2oat时会校验Dex 文件各个section的合法性,这时候使用的compiler filter 为了照顾安装速度等方面,并没有采用全量编译,当app盘启动后,运行一段时间后,收集了足够多的jit 热点方法信息,Android会在后台重新进行dex2oat, 将热点方法编译成机器代码,这时候就不用再重复做验证Dex文件的过程了,
3、odex文件
odex
是优化版的dex
。
在Android N
之前,Dalvik
虚拟机执行程序dex
文件前,系统会对dex
文件做优化,生成可执行文件odex
,保存到data/dalvik-cache
目录,最后把apk
文件中的dex
文件删除。
在Android O
之后,odex
是从vdex
这个文件中 提取了部分模块生成的一个新的 可执行二进制码 文件 , odex
从vdex
中提取后,vdex
的大小就减少了。具体过程:
1.第一次开机就会生成在/system/app/<packagename>/oat/
下
2.在系统运行过程中,虚拟机将其 从“/system/app”
下 copy
到 “/data/davilk-cache/”
下
3.odex + vdex = apk
的全部源码 (vdex
并不是独立于odex
的文件,odex + vdex
才代表一个apk
)
4、oat文件
ART
虚拟机使用的是oat
文件,oat
文件是一种Android
私有ELF
文件格式,它不仅包含有从DEX
文件翻译而来的本地机器指令,还包含有原来的DEX
文件内容。APK
在安装的过程中,会通过dex2oat
工具生成一个OAT
文件。对于APK
来说,oat
文件实际上就是对odex
文件的包装,即oat=odex
,而对于一些framework
中的一些jar
包,会生成相应的oat尾缀的文件,如system@framework@boot-telephony-common.oat
。