1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Field; 18*795d594fSAndroid Build Coastguard Worker import java.lang.reflect.Method; 19*795d594fSAndroid Build Coastguard Worker import java.util.List; 20*795d594fSAndroid Build Coastguard Worker 21*795d594fSAndroid Build Coastguard Worker class MyClassLoader extends ClassLoader { MyClassLoader()22*795d594fSAndroid Build Coastguard Worker MyClassLoader() throws Exception { 23*795d594fSAndroid Build Coastguard Worker super(MyClassLoader.class.getClassLoader()); 24*795d594fSAndroid Build Coastguard Worker 25*795d594fSAndroid Build Coastguard Worker // Some magic to get access to the pathList field of BaseDexClassLoader. 26*795d594fSAndroid Build Coastguard Worker ClassLoader loader = getClass().getClassLoader(); 27*795d594fSAndroid Build Coastguard Worker Class<?> baseDexClassLoader = loader.getClass().getSuperclass(); 28*795d594fSAndroid Build Coastguard Worker Field f = baseDexClassLoader.getDeclaredField("pathList"); 29*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 30*795d594fSAndroid Build Coastguard Worker Object pathList = f.get(loader); 31*795d594fSAndroid Build Coastguard Worker 32*795d594fSAndroid Build Coastguard Worker // Some magic to get access to the dexField field of pathList. 33*795d594fSAndroid Build Coastguard Worker f = pathList.getClass().getDeclaredField("dexElements"); 34*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 35*795d594fSAndroid Build Coastguard Worker dexElements = (Object[]) f.get(pathList); 36*795d594fSAndroid Build Coastguard Worker dexFileField = dexElements[0].getClass().getDeclaredField("dexFile"); 37*795d594fSAndroid Build Coastguard Worker dexFileField.setAccessible(true); 38*795d594fSAndroid Build Coastguard Worker } 39*795d594fSAndroid Build Coastguard Worker 40*795d594fSAndroid Build Coastguard Worker Object[] dexElements; 41*795d594fSAndroid Build Coastguard Worker Field dexFileField; 42*795d594fSAndroid Build Coastguard Worker 43*795d594fSAndroid Build Coastguard Worker static ClassLoader level1ClassLoader; 44*795d594fSAndroid Build Coastguard Worker loadClass(String className, boolean resolve)45*795d594fSAndroid Build Coastguard Worker protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { 46*795d594fSAndroid Build Coastguard Worker if (this != level1ClassLoader) { 47*795d594fSAndroid Build Coastguard Worker if (className.equals("Level1")) { 48*795d594fSAndroid Build Coastguard Worker return level1ClassLoader.loadClass(className); 49*795d594fSAndroid Build Coastguard Worker } else if (className.equals("Level2")) { 50*795d594fSAndroid Build Coastguard Worker throw new ClassNotFoundException("None of my methods require Level2!"); 51*795d594fSAndroid Build Coastguard Worker } else if (!className.equals("LoadedByMyClassLoader")) { 52*795d594fSAndroid Build Coastguard Worker // We're only going to handle LoadedByMyClassLoader. 53*795d594fSAndroid Build Coastguard Worker return getParent().loadClass(className); 54*795d594fSAndroid Build Coastguard Worker } 55*795d594fSAndroid Build Coastguard Worker } else { 56*795d594fSAndroid Build Coastguard Worker if (className != "Level1" && className != "Level2") { 57*795d594fSAndroid Build Coastguard Worker return getParent().loadClass(className); 58*795d594fSAndroid Build Coastguard Worker } 59*795d594fSAndroid Build Coastguard Worker } 60*795d594fSAndroid Build Coastguard Worker 61*795d594fSAndroid Build Coastguard Worker // Mimic what DexPathList.findClass is doing. 62*795d594fSAndroid Build Coastguard Worker try { 63*795d594fSAndroid Build Coastguard Worker for (Object element : dexElements) { 64*795d594fSAndroid Build Coastguard Worker Object dex = dexFileField.get(element); 65*795d594fSAndroid Build Coastguard Worker Method method = dex.getClass().getDeclaredMethod( 66*795d594fSAndroid Build Coastguard Worker "loadClassBinaryName", String.class, ClassLoader.class, List.class); 67*795d594fSAndroid Build Coastguard Worker 68*795d594fSAndroid Build Coastguard Worker if (dex != null) { 69*795d594fSAndroid Build Coastguard Worker Class<?> clazz = (Class<?>) method.invoke(dex, className, this, null); 70*795d594fSAndroid Build Coastguard Worker if (clazz != null) { 71*795d594fSAndroid Build Coastguard Worker return clazz; 72*795d594fSAndroid Build Coastguard Worker } 73*795d594fSAndroid Build Coastguard Worker } 74*795d594fSAndroid Build Coastguard Worker } 75*795d594fSAndroid Build Coastguard Worker } catch (Exception e) { /* Ignore */ } 76*795d594fSAndroid Build Coastguard Worker return null; 77*795d594fSAndroid Build Coastguard Worker } 78*795d594fSAndroid Build Coastguard Worker } 79*795d594fSAndroid Build Coastguard Worker 80*795d594fSAndroid Build Coastguard Worker class LoadedByMyClassLoader { bar()81*795d594fSAndroid Build Coastguard Worker public static void bar() { 82*795d594fSAndroid Build Coastguard Worker Level1.$inline$bar(); 83*795d594fSAndroid Build Coastguard Worker } 84*795d594fSAndroid Build Coastguard Worker } 85*795d594fSAndroid Build Coastguard Worker 86*795d594fSAndroid Build Coastguard Worker class Main { main(String[] args)87*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) throws Exception { 88*795d594fSAndroid Build Coastguard Worker System.loadLibrary(args[0]); 89*795d594fSAndroid Build Coastguard Worker // Clone resolved methods, to restore the original version just 90*795d594fSAndroid Build Coastguard Worker // before we walk the stack in $noinline$bar. 91*795d594fSAndroid Build Coastguard Worker savedResolvedMethods = cloneResolvedMethods(Main.class); 92*795d594fSAndroid Build Coastguard Worker 93*795d594fSAndroid Build Coastguard Worker MyClassLoader o = new MyClassLoader(); 94*795d594fSAndroid Build Coastguard Worker MyClassLoader.level1ClassLoader = new MyClassLoader(); 95*795d594fSAndroid Build Coastguard Worker Class<?> foo = o.loadClass("LoadedByMyClassLoader"); 96*795d594fSAndroid Build Coastguard Worker Method m = foo.getDeclaredMethod("bar"); 97*795d594fSAndroid Build Coastguard Worker try { 98*795d594fSAndroid Build Coastguard Worker m.invoke(null); 99*795d594fSAndroid Build Coastguard Worker } catch (Error e) { /* Ignore */ } 100*795d594fSAndroid Build Coastguard Worker } 101*795d594fSAndroid Build Coastguard Worker $inline$bar()102*795d594fSAndroid Build Coastguard Worker public static void $inline$bar() { 103*795d594fSAndroid Build Coastguard Worker } 104*795d594fSAndroid Build Coastguard Worker $noinline$bar()105*795d594fSAndroid Build Coastguard Worker public static void $noinline$bar() { 106*795d594fSAndroid Build Coastguard Worker try { 107*795d594fSAndroid Build Coastguard Worker // Be evil and clear all dex cache entries. 108*795d594fSAndroid Build Coastguard Worker Field f = Class.class.getDeclaredField("dexCache"); 109*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 110*795d594fSAndroid Build Coastguard Worker Object dexCache = f.get(Main.class); 111*795d594fSAndroid Build Coastguard Worker f = dexCache.getClass().getDeclaredField("resolvedTypes"); 112*795d594fSAndroid Build Coastguard Worker f.setAccessible(true); 113*795d594fSAndroid Build Coastguard Worker Object[] array = (Object[]) f.get(dexCache); 114*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < array.length; i++) { 115*795d594fSAndroid Build Coastguard Worker array[i] = null; 116*795d594fSAndroid Build Coastguard Worker } 117*795d594fSAndroid Build Coastguard Worker restoreResolvedMethods(Main.class, savedResolvedMethods); 118*795d594fSAndroid Build Coastguard Worker } catch (Throwable t) { /* Ignore */ } 119*795d594fSAndroid Build Coastguard Worker 120*795d594fSAndroid Build Coastguard Worker // This will walk the stack, trying to resolve methods in it. 121*795d594fSAndroid Build Coastguard Worker // Because we cleared dex cache entries, we will have to find 122*795d594fSAndroid Build Coastguard Worker // classes again, which require to use the correct class loader 123*795d594fSAndroid Build Coastguard Worker // in the presence of inlining. 124*795d594fSAndroid Build Coastguard Worker new Exception().printStackTrace(System.out); 125*795d594fSAndroid Build Coastguard Worker } 126*795d594fSAndroid Build Coastguard Worker static Object savedResolvedMethods; 127*795d594fSAndroid Build Coastguard Worker cloneResolvedMethods(Class<?> cls)128*795d594fSAndroid Build Coastguard Worker static native Object cloneResolvedMethods(Class<?> cls); restoreResolvedMethods(Class<?> cls, Object saved)129*795d594fSAndroid Build Coastguard Worker static native void restoreResolvedMethods(Class<?> cls, Object saved); 130*795d594fSAndroid Build Coastguard Worker } 131