1 /*
2  * Copyright (C) 2019 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 package art;
18 
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Method;
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Base64;
25 
26 public final class Test1963 {
27   private static boolean IS_ART = System.getProperty("java.vm.name").equals("Dalvik");
28 
29   private static String TEST_CLASS_NAME = "foobar.TestClass";
30   private static String NEW_CLASS_NAME = "foobar.NewClass";
31 
32   /**
33    * base64 encoded class/dex file for
34    * package foobar;
35    * public class NewClass {
36    *   static void sayHi() {
37    *    System.out.println("Hello from NewClass sayHi function");
38    *    TestClass.sayBye();
39    *   }
40    * }
41    */
42   private static byte[] NEW_CLASS_BYTES = Base64.getDecoder().decode(
43       "yv66vgAAADUAIQoABwAPCQAQABEIABIKABMAFAoAFQAWBwAXBwAYAQAGPGluaXQ+AQADKClWAQAE"
44       + "Q29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAKU291cmNlRmlsZQEADU5ld0NsYXNzLmph"
45       + "dmEMAAgACQcAGQwAGgAbAQAiSGVsbG8gZnJvbSBOZXdDbGFzcyBzYXlIaSBmdW5jdGlvbgcAHAwA"
46       + "HQAeBwAfDAAgAAkBAA9mb29iYXIvTmV3Q2xhc3MBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9s"
47       + "YW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRT"
48       + "dHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAQZm9vYmFyL1Rlc3RDbGFz"
49       + "cwEABnNheUJ5ZQAhAAYABwAAAAAAAgABAAgACQABAAoAAAAdAAEAAQAAAAUqtwABsQAAAAEACwAA"
50       + "AAYAAQAAAAIACAAMAAkAAQAKAAAALAACAAAAAAAMsgACEgO2AAS4AAWxAAAAAQALAAAADgADAAAA"
51       + "BAAIAAUACwAGAAEADQAAAAIADg==");
52   private static byte[] NEW_DEX_BYTES = Base64.getDecoder().decode(
53       "ZGV4CjAzNQA8kzH5IALCWT88v716WlU7OfqukCT2o6WQAwAAcAAAAHhWNBIAAAAAAAAAAOQCAAAQ"
54       + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAABAAAA5AAAAAUAAADsAAAAAQAAABQBAABcAgAANAEAAIIB"
55       + "AACKAQAArgEAAMEBAADVAQAA7AEAAAACAAAUAgAAKAIAADcCAAA6AgAAPgIAAEMCAABMAgAAVAIA"
56       + "AFsCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAAB8AQAA"
57       + "BQACAAsAAAAAAAAAAAAAAAAAAAAOAAAAAQAAAA0AAAACAAEADAAAAAMAAAAAAAAAAAAAAAEAAAAD"
58       + "AAAAAAAAAAgAAAAAAAAA0gIAAAAAAAABAAEAAQAAAHIBAAAEAAAAcBAEAAAADgACAAAAAgAAAHYB"
59       + "AAALAAAAYgAAABoBAQBuIAMAEABxAAIAAAAOAAIADgAEAA54PAABAAAABAAGPGluaXQ+ACJIZWxs"
60       + "byBmcm9tIE5ld0NsYXNzIHNheUhpIGZ1bmN0aW9uABFMZm9vYmFyL05ld0NsYXNzOwASTGZvb2Jh"
61       + "ci9UZXN0Q2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAS"
62       + "TGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsADU5ld0NsYXNzLmphdmEAAVYA"
63       + "AlZMAANvdXQAB3ByaW50bG4ABnNheUJ5ZQAFc2F5SGkAdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUi"
64       + "OiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiZDMyODJiOGY1NDdjMjM0YzRlNGM5MzA5YzM2"
65       + "Yzc5NWEyOTg1NmVhYiIsInZlcnNpb24iOiIxLjYuMS1kZXYifQAAAAIAAIGABLQCAQjMAgAAAAAO"
66       + "AAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAEA"
67       + "AADkAAAABQAAAAUAAADsAAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAByAQAAARAA"
68       + "AAEAAAB8AQAAAiAAABAAAACCAQAAACAAAAEAAADSAgAAAxAAAAEAAADgAgAAABAAAAEAAADkAgAA");
69   /**
70    * base64 encoded class/dex file for
71    * package foobar;
72    * public class TestClass {
73    *   public static void sayHi() {
74    *    System.out.println("Hello again from TestClass sayHi function");
75    *    TestClass.sayBye();
76    *   }
77    *   static void sayBye() {
78    *    System.out.println("Goodbye from TestClass!");
79    *   }
80    * }
81    */
82   private static byte[] CLASS_BYTES = Base64.getDecoder().decode(
83       "yv66vgAAADUAIQoACAARCQASABMIABQKABUAFgoABwAXCAAYBwAZBwAaAQAGPGluaXQ+AQADKClW"
84       + "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAGc2F5QnllAQAKU291cmNlRmlsZQEA"
85       + "DlRlc3RDbGFzcy5qYXZhDAAJAAoHABsMABwAHQEAI0hlbGxvIGZyb20gVGVzdENsYXNzIHNheUhp"
86       + "IGZ1bmN0aW9uBwAeDAAfACAMAA4ACgEAF0dvb2RieWUgZnJvbSBUZXN0Q2xhc3MhAQAQZm9vYmFy"
87       + "L1Rlc3RDbGFzcwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAV"
88       + "TGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUo"
89       + "TGphdmEvbGFuZy9TdHJpbmc7KVYAIQAHAAgAAAAAAAMAAQAJAAoAAQALAAAAHQABAAEAAAAFKrcA"
90       + "AbEAAAABAAwAAAAGAAEAAAACAAkADQAKAAEACwAAACwAAgAAAAAADLIAAhIDtgAEuAAFsQAAAAEA"
91       + "DAAAAA4AAwAAAAQACAAFAAsABgAIAA4ACgABAAsAAAAlAAIAAAAAAAmyAAISBrYABLEAAAABAAwA"
92       + "AAAKAAIAAAAIAAgACQABAA8AAAACABA=");
93 
94   private static byte[] DEX_BYTES = Base64.getDecoder().decode(
95       "ZGV4CjAzNQARmtFTPdWXebnrTNy5b71tEiJKC96qIPXAAwAAcAAAAHhWNBIAAAAAAAAAABQDAAAQ"
96       + "AAAAcAAAAAYAAACwAAAAAgAAAMgAAAABAAAA4AAAAAUAAADoAAAAAQAAABABAACQAgAAMAEAAKYB"
97       + "AACuAQAAxwEAAOwBAAAAAgAAFwIAACsCAAA/AgAAUwIAAGMCAABmAgAAagIAAG8CAAB4AgAAgAIA"
98       + "AIcCAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABQAAAAAAAAAKAAAABQAAAKABAAAEAAEA"
99       + "CwAAAAAAAAAAAAAAAAAAAA0AAAAAAAAADgAAAAEAAQAMAAAAAgAAAAAAAAAAAAAAAQAAAAIAAAAA"
100       + "AAAACAAAAAAAAAD+AgAAAAAAAAEAAQABAAAAjgEAAAQAAABwEAQAAAAOAAIAAAACAAAAkgEAAAgA"
101       + "AABiAAAAGgEBAG4gAwAQAA4AAgAAAAIAAACXAQAACwAAAGIAAAAaAQIAbiADABAAcQABAAAADgAC"
102       + "AA4ACAAOeAAEAA54PAAAAAABAAAAAwAGPGluaXQ+ABdHb29kYnllIGZyb20gVGVzdENsYXNzIQAj"
103       + "SGVsbG8gZnJvbSBUZXN0Q2xhc3Mgc2F5SGkgZnVuY3Rpb24AEkxmb29iYXIvVGVzdENsYXNzOwAV"
104       + "TGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3Ry"
105       + "aW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AA5UZXN0Q2xhc3MuamF2YQABVgACVkwAA291dAAHcHJp"
106       + "bnRsbgAGc2F5QnllAAVzYXlIaQB1fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWlu"
107       + "LWFwaSI6MSwic2hhLTEiOiJkMzI4MmI4ZjU0N2MyMzRjNGU0YzkzMDljMzZjNzk1YTI5ODU2ZWFi"
108       + "IiwidmVyc2lvbiI6IjEuNi4xLWRldiJ9AAAAAwAAgYAEsAIBCMgCAQnoAgAAAAAOAAAAAAAAAAEA"
109       + "AAAAAAAAAQAAABAAAABwAAAAAgAAAAYAAACwAAAAAwAAAAIAAADIAAAABAAAAAEAAADgAAAABQAA"
110       + "AAUAAADoAAAABgAAAAEAAAAQAQAAASAAAAMAAAAwAQAAAyAAAAMAAACOAQAAARAAAAEAAACgAQAA"
111       + "AiAAABAAAACmAQAAACAAAAEAAAD+AgAAAxAAAAEAAAAQAwAAABAAAAEAAAAUAwAA");
112   /**
113    * base64 encoded class/dex file for
114    * package foobar;
115    * public class TestClass {
116    *   public static void sayHi() {
117    *    System.out.println("Hello again from TestClass sayHi function");
118    *    NewClass.sayHi();
119    *   }
120    *   static void sayBye() {
121    *    System.out.println("Goodbye again from TestClass!");
122    *   }
123    * }
124    */
125   private static byte[] REDEF_CLASS_BYTES = Base64.getDecoder().decode(
126       "yv66vgAAADUAIwoACAARCQASABMIABQKABUAFgoAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW"
127       + "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAGc2F5QnllAQAKU291cmNlRmlsZQEA"
128       + "DlRlc3RDbGFzcy5qYXZhDAAJAAoHABwMAB0AHgEAKUhlbGxvIGFnYWluIGZyb20gVGVzdENsYXNz"
129       + "IHNheUhpIGZ1bmN0aW9uBwAfDAAgACEHACIMAA0ACgEAHUdvb2RieWUgYWdhaW4gZnJvbSBUZXN0"
130       + "Q2xhc3MhAQAQZm9vYmFyL1Rlc3RDbGFzcwEAEGphdmEvbGFuZy9PYmplY3QBABBqYXZhL2xhbmcv"
131       + "U3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVh"
132       + "bQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAA9mb29iYXIvTmV3Q2xhc3MAIQAH"
133       + "AAgAAAAAAAMAAQAJAAoAAQALAAAAHQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAACAAkADQAK"
134       + "AAEACwAAACwAAgAAAAAADLIAAhIDtgAEuAAFsQAAAAEADAAAAA4AAwAAAAQACAAFAAsABgAIAA4A"
135       + "CgABAAsAAAAlAAIAAAAAAAmyAAISBrYABLEAAAABAAwAAAAKAAIAAAAIAAgACQABAA8AAAACABA=");
136 
137   private static byte[] REDEF_DEX_BYTES = Base64.getDecoder().decode(
138       "ZGV4CjAzNQA2plEeYRH4vl6wJgnAZOVcZ537QN9NXB3wAwAAcAAAAHhWNBIAAAAAAAAAAEQDAAAR"
139       + "AAAAcAAAAAcAAAC0AAAAAgAAANAAAAABAAAA6AAAAAYAAADwAAAAAQAAACABAACwAgAAQAEAALYB"
140       + "AAC+AQAA3QEAAAgCAAAbAgAALwIAAEYCAABaAgAAbgIAAIICAACSAgAAlQIAAJkCAACeAgAApwIA"
141       + "AK8CAAC2AgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA"
142       + "sAEAAAUAAgAMAAAAAAAAAA8AAAABAAAAAAAAAAEAAAAOAAAAAQAAAA8AAAACAAEADQAAAAMAAAAA"
143       + "AAAAAQAAAAEAAAADAAAAAAAAAAkAAAAAAAAALQMAAAAAAAABAAEAAQAAAJ4BAAAEAAAAcBAFAAAA"
144       + "DgACAAAAAgAAAKIBAAAIAAAAYgAAABoBAQBuIAQAEAAOAAIAAAACAAAApwEAAAsAAABiAAAAGgEC"
145       + "AG4gBAAQAHEAAAAAAA4AAgAOAAgADngABAAOeDwAAAAAAQAAAAQABjxpbml0PgAdR29vZGJ5ZSBh"
146       + "Z2FpbiBmcm9tIFRlc3RDbGFzcyEAKUhlbGxvIGFnYWluIGZyb20gVGVzdENsYXNzIHNheUhpIGZ1"
147       + "bmN0aW9uABFMZm9vYmFyL05ld0NsYXNzOwASTGZvb2Jhci9UZXN0Q2xhc3M7ABVMamF2YS9pby9Q"
148       + "cmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2"
149       + "YS9sYW5nL1N5c3RlbTsADlRlc3RDbGFzcy5qYXZhAAFWAAJWTAADb3V0AAdwcmludGxuAAZzYXlC"
150       + "eWUABXNheUhpAHV+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJz"
151       + "aGEtMSI6ImQzMjgyYjhmNTQ3YzIzNGM0ZTRjOTMwOWMzNmM3OTVhMjk4NTZlYWIiLCJ2ZXJzaW9u"
152       + "IjoiMS42LjEtZGV2In0AAAADAAGBgATAAgEI2AIBCfgCAAAAAAAOAAAAAAAAAAEAAAAAAAAAAQAA"
153       + "ABEAAABwAAAAAgAAAAcAAAC0AAAAAwAAAAIAAADQAAAABAAAAAEAAADoAAAABQAAAAYAAADwAAAA"
154       + "BgAAAAEAAAAgAQAAASAAAAMAAABAAQAAAyAAAAMAAACeAQAAARAAAAEAAACwAQAAAiAAABEAAAC2"
155       + "AQAAACAAAAEAAAAtAwAAAxAAAAEAAABAAwAAABAAAAEAAABEAwAA");
156 
SafePrintCause(Throwable t)157   public static void SafePrintCause(Throwable t) {
158     StackTraceElement cause = t.getStackTrace()[0];
159     System.out.println(" --- " + t.getClass().getName() + " At " + cause.getClassName() + "." +
160                        cause.getMethodName() + "(" + cause.getFileName() + ":" +
161                        cause.getLineNumber() + ")");
162   }
163 
run()164   public static void run() throws Exception {
165     System.out.println(" - Run while adding new referenced class.");
166     // No exception expected.
167     run(true);
168     System.out.println(" - Run without adding new referenced class.");
169     try {
170       run(false);
171     } catch (Exception e) {
172       if (e.getCause() == null || !(e.getCause() instanceof NoClassDefFoundError)) {
173         throw new Exception("Unexpected error from test!", e);
174       }
175       // Unfortunately art and RI have different messages here so just return the type.
176       System.out.println(" -- Exception caught when running test without new class added! " +
177                          e.getCause().getClass().getName());
178       SafePrintCause(e.getCause());
179     }
180   }
181 
run(boolean add_new)182   public static void run(boolean add_new) throws Exception {
183     ClassLoader cl = getClassLoader();
184     Class<?> target = cl.loadClass(TEST_CLASS_NAME);
185     Method sayHi = target.getDeclaredMethod("sayHi");
186     System.out.println(" -- Running sayHi before redefinition");
187     sayHi.invoke(null);
188     if (add_new) {
189       System.out.println(" -- Adding NewClass to classloader!");
190       addToClassLoader(cl, NEW_CLASS_BYTES, NEW_DEX_BYTES);
191     }
192     System.out.println(" -- Redefine the TestClass");
193     Redefinition.doCommonClassRedefinition(target, REDEF_CLASS_BYTES, REDEF_DEX_BYTES);
194     System.out.println(" -- call TestClass again, now with NewClass refs");
195     sayHi.invoke(null);
196   }
197 
198   public static class ExtensibleClassLoader extends ClassLoader {
199     private byte[] new_class = null;
ExtensibleClassLoader()200     public ExtensibleClassLoader() {
201       super(ExtensibleClassLoader.class.getClassLoader());
202     }
203 
addSingleClass(byte[] bb)204     public void addSingleClass(byte[] bb) {
205       new_class = bb;
206     }
207 
findClass(String name)208     protected Class<?> findClass(String name) throws ClassNotFoundException {
209       if (name.equals(TEST_CLASS_NAME)) {
210         return this.defineClass(TEST_CLASS_NAME, CLASS_BYTES, 0, CLASS_BYTES.length);
211       }
212       if (name.equals(NEW_CLASS_NAME) && new_class != null) {
213         return this.defineClass(name, new_class, 0, new_class.length);
214       } else {
215         return super.findClass(name);
216       }
217     }
218   }
219 
getClassLoader()220   public static ClassLoader getClassLoader() throws Exception {
221     if (!IS_ART) {
222       return new ExtensibleClassLoader();
223     } else {
224       Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
225       Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
226       return (ClassLoader)ctor.newInstance(ByteBuffer.wrap(DEX_BYTES),
227                                            Test1963.class.getClassLoader());
228     }
229   }
230 
addToClassLoader(ClassLoader cl, byte[] class_bytes, byte[] dex_bytes)231   public static void addToClassLoader(ClassLoader cl, byte[] class_bytes, byte[] dex_bytes) {
232     if (IS_ART) {
233       addToClassLoaderNative(cl, ByteBuffer.allocateDirect(dex_bytes.length).put(dex_bytes));
234     } else {
235       ((ExtensibleClassLoader)cl).addSingleClass(class_bytes);
236     }
237   }
238 
addToClassLoaderNative(ClassLoader loader, ByteBuffer buff)239   public static native void addToClassLoaderNative(ClassLoader loader, ByteBuffer buff);
240 }
241