作者:林作健
UC内核在Android 12上发现一个致命的崩溃。约有10%的用户在冷启动的时候会遇到这个问题,严重影响了UC内核的发布。它的调用栈是这样的:
10-12 19:03:21.461 1038 2723 I id.AlipayGphon: Rejecting re-init on previously-failed class java.lang.Class<com.uc.webkit.impl.WebViewChromiumFactoryProvider>: java.lang.VerifyError: Verifier rejected class com.uc.webkit.impl.WebViewChromiumFactoryProvider: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g() failed to verify: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g(): [0x15] can't resolve returned type 'Unresolved Reference: com.uc.webkit.an' or 'Unresolved Reference: com.uc.webkit.impl.ak' (declaration of 'com.uc.webkit.impl.WebViewChromiumFactoryProvider' appears in /data/user/0/com.eg.android.AlipayGphone/app_h5container/uc/3.22.2.28.21092218119_64/so/core.jar)
10-12 19:03:21.461 1038 2723 I id.AlipayGphon: (Throwable with empty stack trace)
10-12 19:03:21.464 1038 2723 E WebViewEntry: init error and prepare native crash
10-12 19:03:21.464 1038 2723 E WebViewEntry: java.lang.NoClassDefFoundError: com.uc.webkit.impl.WebViewChromiumFactoryProvider
10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.impl.WebViewChromiumFactoryProvider.i(Unknown Source:0)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.WebViewEntry.p(U4Source:193)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.bg.run(Unknown Source:0)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Handler.handleCallback(Handler.java:938)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Handler.dispatchMessage(Handler.java:99)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Looper.loopOnce(Looper.java:201)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Looper.loop(Looper.java:288)
10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.HandlerThread.run(HandlerThread.java:67)
10-12 19:03:21.464 1038 2723 E WebViewEntry: Caused by: java.lang.VerifyError: Verifier rejected class com.uc.webkit.impl.WebViewChromiumFactoryProvider: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g() failed to verify: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g(): [0x15] can't resolve returned type 'Unresolved Reference: com.uc.webkit.an' or 'Unresolved Reference: com.uc.webkit.impl.ak' (declaration of 'com.uc.webkit.impl.WebViewChromiumFactoryProvider' appears in /data/user/0/com.eg.android.AlipayGphone/app_h5container/uc/3.22.2.28.21092218119_64/so/core.jar)
不解决这个问题我们的内核可能无法在Android 12上启用了,对于内核来说又是一个生死攸关的问题。这个问题正常操作无法重现,只能通过monkey疯狂冷启动才能偶现。
另外一个背景是UC浏览器把sdk level提高到了30才引发这个问题。
调用栈分析
从调用栈的信息我们看到最顶层的Error是NoClassDefFoundError,但他是由下面的VerifyError引起的。这个调用栈显示正在进行正常的启动过程。
Rejecting re-init on previously-failed class 显示com.uc.webkit.impl.WebViewChromiumFactoryProvider应该已经尝试过Verify,但是Error了。按照常理应该还有一个VerifyError的抛出。但找了多个崩溃日志都没有发现第一次VerifyError抛出的位置。
另外,这个VerifyError的 Caused by: java.lang.VerifyError位置应该后面还跟着它第一次Verify的调用栈,但它却显示(Throwable with empty stack trace)。
黑科技分析:手段一
带着上述的诸多疑问,我们发现目前的数据不足以我们进行分析,我们需要更多的和Verify有关的信息才能处理问题。
Android的art虚拟机是带着verbose log的。它是按照模块分类的,平时不会打开。需要启动art的时候通过传参让它打开。
我们尝试了wrapper技术,即在lib目录加上文件wrapper.sh,系统就会用wrapper.sh启动虚拟机,而不是通过Zygote。很遗憾这个手段没有作用,分析了AndroidRuntime.cpp里面的源码后,我们发现wrapper传入的虚拟机参赛会被它过滤掉,完全无视。
我们只能使用正经途径之外的方法了。