View Javadoc

    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";//For JDK 5
    44      private final static String DEFINECLASS2_METHOD_NAME = "defineClass2";//For JDK 5
    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                 // store all args in local variables
    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]); // name
    127                 cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
    128                 cv.visitInsn(Constants.ICONST_0); // offset
    129                 cv.visitVarInsn(Constants.ALOAD, locals[1]);
    130                 cv.visitInsn(Constants.ARRAYLENGTH); // length
    131                 cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
    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                 // store all args in local variables
    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]); // name
    160                 cv.visitVarInsn(Constants.ALOAD, locals[1]); // bytes
    161                 cv.visitInsn(Constants.ICONST_0); // offset
    162                 cv.visitVarInsn(Constants.ALOAD, locals[1]);
    163                 cv.visitMethodInsn(Constants.INVOKEVIRTUAL, "Ljava/nio/Buffer;", "remaining", "()I");
    164                 cv.visitVarInsn(Constants.ALOAD, locals[4]); // protection domain
    165                 for (int i = 5; i < args.length; i++) {
    166                     cv.visitVarInsn(args[i].getOpcode(Constants.ILOAD), locals[i]);
    167                 }
    168                 // we should rebuild a new ByteBuffer...
    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 }