1 /***************************************************************************************
2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.hook.impl;
9
10 import org.codehaus.aspectwerkz.hook.ClassLoaderPatcher;
11 import org.codehaus.aspectwerkz.hook.ClassLoaderPreProcessor;
12 import org.objectweb.asm.ClassWriter;
13 import org.objectweb.asm.CodeVisitor;
14 import org.objectweb.asm.Attribute;
15 import org.objectweb.asm.ClassReader;
16 import org.objectweb.asm.ClassVisitor;
17 import org.objectweb.asm.ClassAdapter;
18 import org.objectweb.asm.CodeAdapter;
19 import org.objectweb.asm.Type;
20 import org.objectweb.asm.Constants;
21 import org.objectweb.asm.Label;
22 import org.objectweb.asm.attrs.Attributes;
23
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.FileOutputStream;
27 import java.io.File;
28 import java.util.HashMap;
29 import java.util.Map;
30
31 /***
32 * Instruments the java.lang.ClassLoader to plug in the Class PreProcessor mechanism using ASM. <p/>We are using a
33 * lazy initialization of the class preprocessor to allow all class pre processor logic to be in system classpath and
34 * not in bootclasspath. <p/>This implementation should support IBM custom JRE
35 *
36 * @author Chris Nokleberg
37 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
38 */
39 public class ClassLoaderPreProcessorImpl implements ClassLoaderPreProcessor {
40
41 private final static String CLASSLOADER_CLASS_NAME = "java/lang/ClassLoader";
42 private final static String DEFINECLASS0_METHOD_NAME = "defineClass0";
43 private final static String DEFINECLASS1_METHOD_NAME = "defineClass1";
44 private final static String DEFINECLASS2_METHOD_NAME = "defineClass2";
45
46
47 private static final String DESC_CORE = "Ljava/lang/String;[BIILjava/security/ProtectionDomain;";
48 private static final String DESC_PREFIX = "(" + DESC_CORE;
49 private static final String DESC_HELPER = "(Ljava/lang/ClassLoader;" + DESC_CORE + ")[B";
50
51 private static final String DESC_BYTEBUFFER_CORE = "Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;";
52 private static final String DESC_BYTEBUFFER_PREFIX = "(" + DESC_BYTEBUFFER_CORE;
53 private static final String DESC_BYTEBUFFER_HELPER = "(Ljava/lang/ClassLoader;" + DESC_BYTEBUFFER_CORE + ")[B";
54
55 public ClassLoaderPreProcessorImpl() {
56 }
57
58 /***
59 * Patch caller side of defineClass0
60 * byte[] weaved = ..hook.impl.ClassPreProcessorHelper.defineClass0Pre(this, args..);
61 * klass = defineClass0(name, weaved, 0, weaved.length, protectionDomain);
62 *
63 * @param classLoaderBytecode
64 * @return
65 */
66 public byte[] preProcess(byte[] classLoaderBytecode) {
67 try {
68 ClassWriter cw = new ClassWriter(true);
69 ClassLoaderVisitor cv = new ClassLoaderVisitor(cw);
70 ClassReader cr = new ClassReader(classLoaderBytecode);
71 cr.accept(cv, Attributes.getDefaultAttributes(), false);
72 return cw.toByteArray();
73 } catch (Exception e) {
74 System.err.println("failed to patch ClassLoader:");
75 e.printStackTrace();
76 return classLoaderBytecode;
77 }
78 }
79
80 private static class ClassLoaderVisitor extends ClassAdapter {
81 public ClassLoaderVisitor(ClassVisitor cv) {
82 super(cv);
83 }
84
85 public CodeVisitor visitMethod(int access, String name, String desc, String[] exceptions, Attribute attrs) {
86 CodeVisitor cv = super.visitMethod(access, name, desc, exceptions, attrs);
87 Type[] args = Type.getArgumentTypes(desc);
88 return new PreProcessingVisitor(cv, access, args);
89 }
90 }
91
92 /***
93 * @author Chris Nokleberg
94 */
95 private static class PreProcessingVisitor extends RemappingCodeVisitor {
96 public PreProcessingVisitor(CodeVisitor cv, int access, Type[] args) {
97 super(cv, access, args);
98 }
99
100 public void visitMethodInsn(int opcode, String owner, String name, String desc) {
101 if ((DEFINECLASS0_METHOD_NAME.equals(name) || (DEFINECLASS1_METHOD_NAME.equals(name)))
102 && CLASSLOADER_CLASS_NAME.equals(owner)) {
103 Type[] args = Type.getArgumentTypes(desc);
104 if (args.length < 5 || !desc.startsWith(DESC_PREFIX)) {
105 throw new Error("non supported JDK, native call not supported: " + desc);
106 }
107
108 int[] locals = new int[args.length];
109 for (int i = args.length - 1; i >= 0; i--) {
110 cv.visitVarInsn(
111 args[i].getOpcode(Constants.ISTORE),
112 locals[i] = nextLocal(args[i].getSize())
113 );
114 }
115 for (int i = 0; i < 5; i++) {
116 cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
117 }
118 super.visitMethodInsn(
119 Constants.INVOKESTATIC,
120 "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
121 "defineClass0Pre",
122 DESC_HELPER
123 );
124 cv.visitVarInsn(Constants.ASTORE, locals[1]);
125 cv.visitVarInsn(Constants.ALOAD, 0);
126 cv.visitVarInsn(Constants.ALOAD, locals[0]);
127 cv.visitVarInsn(Constants.ALOAD, locals[1]);
128 cv.visitInsn(Constants.ICONST_0);
129 cv.visitVarInsn(Constants.ALOAD, locals[1]);
130 cv.visitInsn(Constants.ARRAYLENGTH);
131 cv.visitVarInsn(Constants.ALOAD, locals[4]);
132 for (int i = 5; i < args.length; i++) {
133 cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
134 }
135 } else if (DEFINECLASS2_METHOD_NAME.equals(name) && CLASSLOADER_CLASS_NAME.equals(owner)) {
136 Type[] args = Type.getArgumentTypes(desc);
137 if (args.length < 5 || !desc.startsWith(DESC_BYTEBUFFER_PREFIX)) {
138 throw new Error("non supported JDK, bytebuffer native call not supported: " + desc);
139 }
140
141 int[] locals = new int[args.length];
142 for (int i = args.length - 1; i >= 0; i--) {
143 cv.visitVarInsn(
144 args[i].getOpcode(Constants.ISTORE),
145 locals[i] = nextLocal(args[i].getSize())
146 );
147 }
148 for (int i = 0; i < 5; i++) {
149 cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
150 }
151 super.visitMethodInsn(
152 Constants.INVOKESTATIC,
153 "org/codehaus/aspectwerkz/hook/impl/ClassPreProcessorHelper",
154 "defineClass0Pre",
155 DESC_BYTEBUFFER_HELPER
156 );
157 cv.visitVarInsn(Constants.ASTORE, locals[1]);
158 cv.visitVarInsn(Constants.ALOAD, 0);
159 cv.visitVarInsn(Constants.ALOAD, locals[0]);
160 cv.visitVarInsn(Constants.ALOAD, locals[1]);
161 cv.visitInsn(Constants.ICONST_0);
162 cv.visitVarInsn(Constants.ALOAD, locals[1]);
163 cv.visitMethodInsn(Constants.INVOKEVIRTUAL, "Ljava/nio/Buffer;", "remaining", "()I");
164 cv.visitVarInsn(Constants.ALOAD, locals[4]);
165 for (int i = 5; i < args.length; i++) {
166 cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
167 }
168
169
170 }
171 super.visitMethodInsn(opcode, owner, name, desc);
172 }
173 }
174
175 /***
176 * @author Chris Nokleberg
177 */
178 private static class State {
179 Map locals = new HashMap();
180 int firstLocal;
181 int nextLocal;
182
183 State(int access, Type[] args) {
184 nextLocal = ((Constants.ACC_STATIC & access) != 0) ? 0 : 1;
185 for (int i = 0; i < args.length; i++) {
186 nextLocal += args[i].getSize();
187 }
188 firstLocal = nextLocal;
189 }
190 }
191
192 /***
193 * @author Chris Nokleberg
194 */
195 private static class IntRef {
196 int key;
197
198 public boolean equals(Object o) {
199 return key == ((IntRef) o).key;
200 }
201
202 public int hashCode() {
203 return key;
204 }
205 }
206
207 /***
208 * @author Chris Nokleberg
209 */
210 private static class RemappingCodeVisitor extends CodeAdapter {
211 private State state;
212 private IntRef check = new IntRef();
213
214
215 public RemappingCodeVisitor(CodeVisitor v, int access, Type[] args) {
216 super(v);
217 state = new State(access, args);
218 }
219
220 public RemappingCodeVisitor(RemappingCodeVisitor wrap) {
221 super(wrap.cv);
222 this.state = wrap.state;
223 }
224
225 protected int nextLocal(int size) {
226 int var = state.nextLocal;
227 state.nextLocal += size;
228 return var;
229 }
230
231 private int remap(int var, int size) {
232 if (var < state.firstLocal) {
233 return var;
234 }
235 check.key = (size == 2) ? ~var : var;
236 Integer value = (Integer) state.locals.get(check);
237 if (value == null) {
238 IntRef ref = new IntRef();
239 ref.key = check.key;
240 state.locals.put(ref, value = new Integer(nextLocal(size)));
241 }
242 return value.intValue();
243 }
244
245 public void visitIincInsn(int var, int increment) {
246 cv.visitIincInsn(remap(var, 1), increment);
247 }
248
249 public void visitLocalVariable(String name, String desc, Label start, Label end, int index) {
250 cv.visitLocalVariable(name, desc, start, end, remap(index, 0));
251 }
252
253 public void visitVarInsn(int opcode, int var) {
254 int size;
255 switch (opcode) {
256 case Constants.LLOAD:
257 case Constants.LSTORE:
258 case Constants.DLOAD:
259 case Constants.DSTORE:
260 size = 2;
261 break;
262 default:
263 size = 1;
264 }
265 cv.visitVarInsn(opcode, remap(var, size));
266 }
267
268 public void visitMaxs(int maxStack, int maxLocals) {
269 cv.visitMaxs(0, 0);
270 }
271 }
272
273 public static void main(String args[]) throws Exception {
274 ClassLoaderPreProcessor me = new ClassLoaderPreProcessorImpl();
275 InputStream is = ClassLoader.getSystemClassLoader().getParent().getResourceAsStream(
276 "java/lang/ClassLoader.class"
277 );
278 byte[] out = me.preProcess(ClassLoaderPatcher.inputStreamToByteArray(is));
279 is.close();
280 File dir = new File("_boot/java/lang/");
281 dir.mkdirs();
282 OutputStream os = new FileOutputStream("_boot/java/lang/ClassLoader.class");
283 os.write(out);
284 os.close();
285 }
286 }