NoClassDefFoundError
1. Background
1.1 reproducibility
某个apk在androidO的一个分支上启动的时候出现FC
01-06 03:02:51.913 3980 3980 D AndroidRuntime:Shutting down VM
01-06 03:02:51.914 3980 3980 E AndroidRuntime: FATALEXCEPTION: main
01-06 03:02:51.914 3980 3980 E AndroidRuntime:Process: com.chinamworld.main, PID: 3980
01-06 03:02:51.914 3980 3980 E AndroidRuntime:java.lang.NoClassDefFoundError: Failed resolution of:Lcom/ccb/framework/util/CcbLogger;
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atcom.ccb.start.CcbSplashActivity.onCreateOrg(Unknown Source:14)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atcom.ccb.start.CcbSplashActivity.onCreate(Unknown Source:0)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:6999)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.Activity.performCreate(Activity.java:6990)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.ActivityThread.-wrap11(Unknown Source:0)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.os.Handler.dispatchMessage(Handler.java:106)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atandroid.os.Looper.loop(Looper.java:164)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6501)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atjava.lang.reflect.Method.invoke(Native Method)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atcom.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
01-06 03:02:51.914 3980 3980 E AndroidRuntime:Caused by: java.lang.ClassNotFoundException: Didn't find class"com.ccb.framework.util.CcbLogger" on path: DexPathList[[zip file"/data/user/0/com.chinamworld.main/.cache/classes.jar", zip file"/data/app/com.chinamworld.main-sJGleomDgK0_gomW8ZFeTw==/base.apk"],nativeLibraryDirectories=[/data/app/com.chinamworld.main-sJGleomDgK0_gomW8ZFeTw==/lib/arm,/system/fake-libs,/data/app/com.chinamworld.main-sJGleomDgK0_gomW8ZFeTw==/base.apk!/lib/armeabi-v7a,/system/lib, /vendor/lib]]
01-06 03:02:51.914 3980 3980 E AndroidRuntime: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atjava.lang.ClassLoader.loadClass(ClassLoader.java:379)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: atjava.lang.ClassLoader.loadClass(ClassLoader.java:312)
01-06 03:02:51.914 3980 3980 E AndroidRuntime: ... 15 more
2. Analysis
2.1 Step1:
2.1.1 Log1
01-0603:02:51.914 3980 3980 E AndroidRuntime:java.lang.NoClassDefFoundError: Failed resolution of:Lcom/ccb/framework/util/CcbLogger;
从log看,貌似是一个类加载器引起的命名空间问题。
一番分析加载路径,无果。
2.1.2 Log2
再仔细查看整个log,会发现下面一段
01-06 03:02:56.042 4034 4034 W zygote : Opening an oat file without a class loader.Are you using the deprecated DexFile APIs?
01-06 03:02:56.056 4034 4034 W zygote : Skipping duplicate class check due tounsupported classloader
01-06 03:02:56.059 4034 4034 W zygote : Opening an oat file without a class loader.Are you using the deprecated DexFile APIs?
01-06 03:02:56.062 4034 4034 I System.out:java.io.IOException: No original dex files found for dex location/data/user/0/com.chinamworld.main/.cache/classes.jar
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexFile.openDexFileNative(Native Method)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexFile.openDexFile(DexFile.java:353)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexFile.<init>(DexFile.java:142)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexFile.loadDex(DexFile.java:201)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexPathList.loadDexFile(DexPathList.java:377)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexPathList.makeDexElements(DexPathList.java:337)
01-06 03:02:56.063 4034 4034 I System.out: atdalvik.system.DexPathList.makePathElements(DexPathList.java:423)
01-06 03:02:56.063 4034 4034 I System.out: at java.lang.reflect.Method.invoke(NativeMethod)
01-06 03:02:56.063 4034 4034 I System.out: atcom.secneo.apkwrapper.DexInstall$V19.makeDexElements(DexInstall.java:269)
01-06 03:02:56.063 4034 4034 I System.out: at com.secneo.apkwrapper.DexInstall$V19.install(DexInstall.java:216)
01-06 03:02:56.063 4034 4034 I System.out: atcom.secneo.apkwrapper.DexInstall$V19.access$100(DexInstall.java:201)
01-06 03:02:56.063 4034 4034 I System.out: atcom.secneo.apkwrapper.DexInstall.installSecondaryDexes(DexInstall.java:67)
01-06 03:02:56.063 4034 4034 I System.out: atcom.secneo.apkwrapper.DexInstall.install(DexInstall.java:32)
01-06 03:02:56.063 4034 4034 I System.out: at java.lang.Runtime.nativeLoad(NativeMethod)
01-06 03:02:56.063 4034 4034 I System.out: atjava.lang.Runtime.doLoad(Runtime.java:1099)
01-06 03:02:56.063 4034 4034 I System.out: atjava.lang.Runtime.loadLibrary0(Runtime.java:1014)
01-06 03:02:56.064 4034 4034 I System.out: at java.lang.System.loadLibrary(System.java:1657)
01-06 03:02:56.064 4034 4034 I System.out: atcom.secneo.apkwrapper.ApplicationWrapper.<clinit>(ApplicationTemplate.java:27)
01-06 03:02:56.064 4034 4034 I System.out: at java.lang.Class.newInstance(NativeMethod)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.Instrumentation.newApplication(Instrumentation.java:1103)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.Instrumentation.newApplication(Instrumentation.java:1088)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.LoadedApk.makeApplication(LoadedApk.java:983)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.ActivityThread.handleBindApplication(ActivityThread.java:5722)
01-06 03:02:56.064 4034 4034 I System.out: at android.app.ActivityThread.-wrap1(UnknownSource:0)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1656)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.os.Handler.dispatchMessage(Handler.java:106)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.os.Looper.loop(Looper.java:164)
01-06 03:02:56.064 4034 4034 I System.out: atandroid.app.ActivityThread.main(ActivityThread.java:6501)
01-06 03:02:56.064 4034 4034 I System.out: at java.lang.reflect.Method.invoke(NativeMethod)
从调用栈可以看出,这个是应用进程启动过程中,创建application的时候,加载了自定义的库文件,并且在该库文件中,用反射的方法加载multidex,最终由于在反射的时候使用了一个deprecatedDexFile API makePathElements()实现自己的类加载器,这个api进一步调用了makeDexElements(List<File>files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader),传入的ClassLoader为null,在后面的OatFileManager::OpenDexFilesFromOat里面进行ClassLoader 检查的时候发现异常,导致加载dex失败,从而后续的类加载失败。
2.2 Step2:
2.2.1 Code
从最新代码开不出问题所在,比较出问题的代码分支,可以看出差异,
再结合类加载器的创建过程和application的调用栈,问题就很明显了。
2.2.2 代码文件名
oat_file_manager.cc
dalvik_system_DexFile.cc
BaseDexClassLoader.java
DexPathList.java
DexFile.java
3. RootCause
这个是应用进程启动过程中,创建application的时候,加载了自定义的库文件,并且在该库文件中,用反射的方法加载multidex,最终由于在反射的时候使用了一个deprecatedDexFile API makePathElements()实现自己的类加载器,这个api进一步调用了makeDexElements(List<File>files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader),传入的ClassLoader为null,在后面的OatFileManager::OpenDexFilesFromOat里面进行ClassLoader 检查的时候发现异常,导致加载dex失败,从而后续的类加载失败。
4. Solution
4.1 Way 1 apk side
Apk should update it’s code by replacing the deprecatedDexFile API makePathElements().
4.2 Way 2 AOSP side
The art should sync the code with the mainline