changeset 55:80952fd2bbd4

Implemented rewriting of GOTO and IF_*Z instructions. Contributed-by: Remi Forax * rewriter/DexRewriter.java (visitInstrGoto, visitInstrIfTestZ): Implemented. * rewriter/Interpreter.java (cloneJoinPoint): Allows to join control flow. * rewriter/Main.java: Switched to our own parser implementation. * rewriter/Patchable.java (union): New method for joining two patchable instructions.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Sun, 20 Mar 2011 19:51:58 +0100
parents 3e27a236f4b7
children ef7e8137da03
files src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java src/main/java/org/icedrobot/daneel/rewriter/Main.java src/main/java/org/icedrobot/daneel/rewriter/Patchable.java src/main/java/org/icedrobot/daneel/rewriter/Register.java
diffstat 5 files changed, 107 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Sun Mar 20 19:31:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Sun Mar 20 19:51:58 2011 +0100
@@ -42,6 +42,7 @@
 import static org.icedrobot.daneel.rewriter.Register.*;
 import static org.icedrobot.daneel.rewriter.Registers.*;
 
+import java.util.HashMap;
 import java.util.Map;
 
 import org.icedrobot.daneel.dex.DexClassVisitor;
@@ -143,6 +144,10 @@
                                         // the return value
         private int locals;             // number of local variables
         private int parameters;         // number of parameters
+        
+        // map DEX jump label to ASM label
+        private final HashMap<Label, org.objectweb.asm.Label> labelMap =
+            new HashMap<Label, org.objectweb.asm.Label>();
 
         public MethodRewriter(MethodVisitor mv, String desc) {
             this.mv = new PatchMethodVisitor(mv);
@@ -196,6 +201,20 @@
             return getTypeFromASMType(Type.getReturnType(descriptor));
         }
 
+        /** Maps DEX label to ASM label
+         * @param label a DEX label
+         * @return the corresponding ASM label.
+         */
+        private org.objectweb.asm.Label getASMLabel(Label label) {
+            org.objectweb.asm.Label asmLabel = labelMap.get(label);
+            if (asmLabel != null) {
+                return asmLabel;
+            }
+            asmLabel = new org.objectweb.asm.Label();
+            labelMap.put(label, asmLabel);
+            return asmLabel;
+        }
+        
         private static AssertionError newAssertionError(Opcode opcode) {
             return new AssertionError("Invalid opcode: " + opcode);
         }
@@ -428,10 +447,57 @@
             }
         }
         
-        // unimplemented
         @Override
         public void visitLabel(Label label) {
-            // XXX Ignore labels for now.
+            Register[] registers = interpreter.getJoinPoint(label);   
+            if (registers != null) { // target of an already seen jump
+                interpreter.merge(registers);
+            } else {  // perhaps a control flow join point, save state
+                interpreter.cloneJoinPoint(label);
+            }
+            mv.visitLabel(getASMLabel(label));
+        }
+        
+        @Override
+        public void visitInstrGoto(Opcode opcode, Label label) {
+            Register[] registers = interpreter.getJoinPoint(label);
+            if (registers != null) { // backward goto
+                interpreter.merge(registers);
+            } else {  // forward goto
+                interpreter.cloneJoinPoint(label);    
+            }
+            
+            mv.visitJumpInsn(GOTO, getASMLabel(label));
+            interpreter.setDead();
+        }
+        
+        @Override
+        public void visitInstrIfTestZ(Opcode opcode, int vsrc, Label label) {
+            vsrc = registerToSlot(vsrc);
+            mv.visitVarInsn(ILOAD, vsrc);
+            interpreter.load(vsrc, INT_TYPE);
+            
+            Register[] registers = interpreter.getJoinPoint(label);
+            if (registers != null) { // backward jump
+                interpreter.merge(registers);
+            } else {  // forward jump
+                interpreter.cloneJoinPoint(label);    
+            }
+            
+            org.objectweb.asm.Label asmLabel = getASMLabel(label);
+            int javaOpcode = IFEQ + opcode.ordinal() - Opcode.IF_EQZ.ordinal();
+            mv.visitJumpInsn(javaOpcode, asmLabel);
+        }
+        
+        
+        // unimplemented
+        
+        @Override
+        public void visitInstrIfTest(Opcode opcode, int vsrc1, int vsrc2,
+                Label label) {
+            vsrc1 = registerToSlot(vsrc1);
+            vsrc2 = registerToSlot(vsrc2);
+            throw new UnsupportedOperationException("NYI " + opcode);
         }
 
         @Override
@@ -473,25 +539,6 @@
         }
 
         @Override
-        public void visitInstrGoto(Opcode opcode, Label label) {
-            throw new UnsupportedOperationException("NYI " + opcode);
-        }
-
-        @Override
-        public void visitInstrIfTest(Opcode opcode, int vsrc1, int vsrc2,
-                Label label) {
-            vsrc1 = registerToSlot(vsrc1);
-            vsrc2 = registerToSlot(vsrc2);
-            throw new UnsupportedOperationException("NYI " + opcode);
-        }
-
-        @Override
-        public void visitInstrIfTestZ(Opcode opcode, int vsrc, Label label) {
-            vsrc = registerToSlot(vsrc);
-            throw new UnsupportedOperationException("NYI " + opcode);
-        }
-
-        @Override
         public void visitInstrInstanceof(Opcode opcode, int vdest, int vsrc,
                 String type) {
             vdest = registerToSlot(vdest);
--- a/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Sun Mar 20 19:31:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Interpreter.java	Sun Mar 20 19:51:58 2011 +0100
@@ -66,12 +66,14 @@
         registers[vregister].storeArbitraryType(arbitraryType, patchable);
     }
 
-    public void copy(int vdst, int vsrc) {
-        registers[vdst].copy(registers[vsrc]);
-    }
-
-    public void storeJoinPoint(Object label) {
-        joinPointMap.put(label, registers.clone());
+    public void cloneJoinPoint(Object label) {
+        Register[] registers = this.registers;
+        int length = registers.length;
+        Register[] clone = new Register[length];
+        for (int i = 0; i < length; i++) {
+            clone[i] = registers[i].duplicate();
+        }
+        joinPointMap.put(label, clone);
     }
 
     public Register[] getJoinPoint(Object label) {
@@ -90,7 +92,7 @@
         assert this.registers.length == registers.length;
 
         if (isDead) {
-            System.arraycopy(registers, registers.length, this.registers, 0,
+            System.arraycopy(registers, 0, this.registers, 0,
                     registers.length);
             isDead = false;
             return;
--- a/src/main/java/org/icedrobot/daneel/rewriter/Main.java	Sun Mar 20 19:31:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Main.java	Sun Mar 20 19:51:58 2011 +0100
@@ -44,6 +44,7 @@
 import java.lang.reflect.Method;
 import java.util.Arrays;
 
+import org.icedrobot.daneel.dex.DexFile;
 import org.icedrobot.daneel.dex.DexReader;
 import org.icedrobot.daneel.dex.dexlib.DexLibDexReader;
 import org.objectweb.asm.ClassReader;
@@ -58,14 +59,15 @@
     public static void main(String[] args) throws IOException,
             ClassNotFoundException, NoSuchMethodException,
             IllegalAccessException {
-        final DexReader dexReader = new DexLibDexReader(new File(args[0]));
-
+        //final DexReader dexReader = new DexLibDexReader(new File(args[0]));
+        final DexReader dexReader = DexFile.parse(new File(args[0]));
+        
         ClassLoader classLoader = new ClassLoader() {
             @Override
             protected Class<?> findClass(String name)
                     throws ClassNotFoundException {
                 ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-                dexReader.accept(name, new DexRewriter(writer));
+                dexReader.accept('L'+name.replace('.', '/')+';', new DexRewriter(writer));
                 byte[] array = writer.toByteArray();
 
                 CheckClassAdapter.verify(new ClassReader(array), true,
--- a/src/main/java/org/icedrobot/daneel/rewriter/Patchable.java	Sun Mar 20 19:31:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Patchable.java	Sun Mar 20 19:51:58 2011 +0100
@@ -37,7 +37,7 @@
 
 package org.icedrobot.daneel.rewriter;
 
-public abstract class Patchable {
+abstract class Patchable {
     private boolean patchCalled;
 
     void doPatch(int registerType) {
@@ -48,4 +48,16 @@
     }
 
     protected abstract void patch(int registerType);
+
+    public static Patchable union(final Patchable patchable1, final Patchable patchable2) {
+        patchable1.getClass();  // nullcheck
+        patchable2.getClass();  // nullcheck
+        return new Patchable() {
+            @Override
+            protected void patch(int registerType) {
+                patchable1.patch(registerType);
+                patchable2.patch(registerType);
+            }
+        };
+    }
 }
--- a/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Sun Mar 20 19:31:39 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/Register.java	Sun Mar 20 19:51:58 2011 +0100
@@ -66,7 +66,7 @@
 
     Register() {
         type = NO_TYPE;
-        // and patchable == null
+        // and patchable = null
     }
 
     public void load(int expectedType) {
@@ -89,9 +89,11 @@
         this.patchable = patchable;
     }
 
-    public void copy(Register register) {
-        this.type = register.type;
-        this.patchable = register.patchable;
+    public Register duplicate() {
+        Register register = new Register();
+        register.type = type;
+        register.patchable = patchable;
+        return register;
     }
 
     public void merge(Register register) {
@@ -106,8 +108,9 @@
 
         if (thisIsArbitrary) {
             if (registerIsArbitrary) {
-                // do nothing, if they are not compatible, the VM verifier will
-                // emit a VerifyError
+                // maybe there are not compatible, but this means that
+                // the code is malformed
+                patchable = Patchable.union(patchable, register.patchable);
                 return;
             }
             if (patchable != null) { // otherwise, it's an uninitialized value
@@ -118,9 +121,9 @@
             return;
         }
 
-        // if register.type is not an arbitrary, this means that the code may be
-        // not verifiable
-        // it's ok if the register is no more used
+        // the two register aren't arbitrary, we don't check is they are compatible
+        // because they can be incompatible if the register is no more used
+        // This should not be the common case because dx remove unused registers
         if (!registerIsArbitrary) {
             return;
         }