Mercurial > hg > icedrobot > daneel
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; }