1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import java.lang.reflect.Field; 18 import java.lang.reflect.Method; 19 import java.util.ArrayList; 20 import java.util.List; 21 22 class MyClassLoader extends ClassLoader { MyClassLoader()23 MyClassLoader() throws Exception { 24 super(MyClassLoader.class.getClassLoader()); 25 26 // Some magic to get access to the pathList field of BaseDexClassLoader. 27 ClassLoader loader = getClass().getClassLoader(); 28 Class<?> baseDexClassLoader = loader.getClass().getSuperclass(); 29 Field f = baseDexClassLoader.getDeclaredField("pathList"); 30 f.setAccessible(true); 31 Object pathList = f.get(loader); 32 33 // Some magic to get access to the dexField field of pathList. 34 // Need to make a copy of the dex elements since we don't want an app image 35 // with pre-resolved things. 36 f = pathList.getClass().getDeclaredField("dexElements"); 37 f.setAccessible(true); 38 Object[] dexElements = (Object[]) f.get(pathList); 39 f = dexElements[0].getClass().getDeclaredField("dexFile"); 40 f.setAccessible(true); 41 for (Object element : dexElements) { 42 Object dexFile = f.get(element); 43 // Make copy. 44 Field fileNameField = dexFile.getClass().getDeclaredField("mFileName"); 45 fileNameField.setAccessible(true); 46 dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance( 47 fileNameField.get(dexFile))); 48 } 49 } 50 51 ArrayList<Object> dexFiles = new ArrayList<Object>(); 52 Field dexFileField; 53 loadClass(String className, boolean resolve)54 protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { 55 // Other classes may also get loaded, ignore those. 56 if (className.equals("LoadedByMyClassLoader") 57 || className.equals("FirstSeenByMyClassLoader")) { 58 System.out.println("Request for " + className); 59 } 60 61 // We're only going to handle LoadedByMyClassLoader. 62 if (className != "LoadedByMyClassLoader") { 63 return getParent().loadClass(className); 64 } 65 66 // Mimic what DexPathList.findClass is doing. 67 try { 68 for (Object dexFile : dexFiles) { 69 Method method = dexFile.getClass().getDeclaredMethod( 70 "loadClassBinaryName", String.class, ClassLoader.class, List.class); 71 72 if (dexFile != null) { 73 Class<?> clazz = (Class<?>) method.invoke(dexFile, className, this, null); 74 if (clazz != null) { 75 return clazz; 76 } 77 } 78 } 79 } catch (Exception e) { /* Ignore */ } 80 return null; 81 } 82 } 83 84 class LoadedByMyClassLoader { 85 /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before) 86 /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader 87 /// CHECK-NEXT: ClinitCheck 88 /// CHECK-NEXT: InvokeStaticOrDirect 89 /// CHECK-NEXT: LoadClass class_name:java.lang.System 90 /// CHECK-NEXT: ClinitCheck 91 /// CHECK-NEXT: StaticFieldGet 92 /// CHECK-NEXT: LoadString 93 /// CHECK-NEXT: NullCheck 94 /// CHECK-NEXT: InvokeVirtual 95 96 /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after) 97 /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader 98 /// CHECK-NEXT: ClinitCheck 99 /* We inlined FirstSeenByMyClassLoader.$inline$bar */ 100 /// CHECK-NEXT: LoadClass class_name:java.lang.System 101 /// CHECK-NEXT: ClinitCheck 102 /// CHECK-NEXT: StaticFieldGet 103 /// CHECK-NEXT: LoadString 104 /// CHECK-NEXT: NullCheck 105 /// CHECK-NEXT: InvokeVirtual 106 107 /// CHECK-START: void LoadedByMyClassLoader.bar() register (before) 108 /* Load and initialize FirstSeenByMyClassLoader */ 109 /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true 110 /* Load and initialize System */ 111 // There may be HX86ComputeBaseMethodAddress here. 112 /// CHECK: LoadClass class_name:java.lang.System 113 // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass. 114 // (The merging checks for environment match but HLoadClass/kBootImageAddress 115 // used for non-PIC mode does not have an environment at all.) 116 /// CHECK: StaticFieldGet 117 // There may be HX86ComputeBaseMethodAddress here. 118 /// CHECK: LoadString 119 /// CHECK-NEXT: NullCheck 120 /// CHECK-NEXT: InvokeVirtual bar()121 public static void bar() { 122 FirstSeenByMyClassLoader.$inline$bar(); 123 System.out.println("In between the two calls."); 124 FirstSeenByMyClassLoader.$noinline$bar(); 125 } 126 } 127 128 public class Main { main(String[] args)129 public static void main(String[] args) throws Exception { 130 MyClassLoader o = new MyClassLoader(); 131 Class<?> foo = o.loadClass("LoadedByMyClassLoader"); 132 Method m = foo.getDeclaredMethod("bar"); 133 m.invoke(null); 134 } 135 } 136