mirror of
https://github.com/revanced/smali.git
synced 2025-06-12 12:17:37 +02:00
Added better support for the case when an optimized instruction can't be resolved due to the object register always being null. It should comment out any dead code following the unresolved instruction (to prevent verification issues, due to the changed method call)
git-svn-id: https://smali.googlecode.com/svn/trunk@455 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
@ -28,30 +28,22 @@
|
||||
|
||||
package org.jf.dexlib.Code.Format;
|
||||
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
|
||||
/**
|
||||
* This represents a "fixed" Format35ms, or Format3rms instruction, where the object register is always null and so the
|
||||
* correct type can't be determined. How this is handled is "implementation dependent". baksmali just replaces it with
|
||||
* a call to object->hashCode(). Since the object register is always null, this will have the same effect as calling
|
||||
* whatever the original method was - namely, a NPE
|
||||
*/
|
||||
public class Instruction35msn extends Instruction {
|
||||
public class DeadInstruction extends Instruction {
|
||||
public final Instruction OriginalInstruction;
|
||||
|
||||
/**
|
||||
* this is the first register, i.e. the object register. The others don't matter, because we don't know what
|
||||
* the original method call is/was.
|
||||
*/
|
||||
public final int RegisterNum;
|
||||
|
||||
public Instruction35msn(int registerNum) {
|
||||
super(Opcode.INVOKE_VIRTUAL);
|
||||
this.RegisterNum = registerNum;
|
||||
public DeadInstruction(Instruction originalInstruction) {
|
||||
super(originalInstruction.opcode);
|
||||
this.OriginalInstruction = originalInstruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return OriginalInstruction.getSize();
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return Format.Format35msn;
|
||||
return Format.DeadInstruction;
|
||||
}
|
||||
}
|
@ -45,7 +45,6 @@ public enum Format {
|
||||
Format22c(Instruction22c.Factory, 4),
|
||||
Format22cs(Instruction22cs.Factory, 4),
|
||||
Format22csf(null, 4),
|
||||
Format22csn(null, 4),
|
||||
Format22s(Instruction22s.Factory, 4),
|
||||
Format22t(Instruction22t.Factory, 4),
|
||||
Format22x(Instruction22x.Factory, 4),
|
||||
@ -60,14 +59,16 @@ public enum Format {
|
||||
Format35sf(null, 6),
|
||||
Format35ms(Instruction35ms.Factory, 6),
|
||||
Format35msf(null, 6),
|
||||
Format35msn(null, 6),
|
||||
Format3rc(Instruction3rc.Factory, 6),
|
||||
Format3rms(Instruction3rms.Factory, 6),
|
||||
Format3rmsf(null, 6),
|
||||
Format51l(Instruction51l.Factory, 10),
|
||||
ArrayData(null, -1, true),
|
||||
PackedSwitchData(null, -1, true),
|
||||
SparseSwitchData(null, -1, true);
|
||||
SparseSwitchData(null, -1, true),
|
||||
UnresolvedNullReference(null, -1, false),
|
||||
DeadInstruction(null, -1, false)
|
||||
;
|
||||
|
||||
public final Instruction.InstructionFactory Factory;
|
||||
public final int size;
|
||||
|
@ -29,30 +29,30 @@
|
||||
package org.jf.dexlib.Code.Format;
|
||||
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
|
||||
/**
|
||||
* This represents a "fixed" Format22cs instruction, where the object register is always null and so the correct type
|
||||
* This represents a "fixed" odexed instruction, where the object register is always null and so the correct type
|
||||
* can't be determined. How this is handled is "implementation dependent". baksmali just replaces it with a call to
|
||||
* object->hashCode(). Since the object register is always null, this will have the same effect as tring to access
|
||||
* whatever field that was trying to be accessed - namely, a NPE
|
||||
* whatever method/field that was trying to be accessed - namely, a NPE
|
||||
*/
|
||||
public class Instruction22csn extends Instruction {
|
||||
public class UnresolvedNullReference extends Instruction {
|
||||
public final Instruction OriginalInstruction;
|
||||
//the register number that holds the (null) reference type that the instruction operates on
|
||||
public final int ObjectRegisterNum;
|
||||
|
||||
/**
|
||||
* this is the first register, i.e. the object register. The others don't matter, because we don't know what
|
||||
* the original field is/was.
|
||||
*/
|
||||
public final int RegisterNum;
|
||||
|
||||
public Instruction22csn(int registerNum) {
|
||||
//the opcode should be ignored. It just needs to be a 4 byte opcode
|
||||
super(Opcode.IGET_QUICK);
|
||||
this.RegisterNum = registerNum;
|
||||
public UnresolvedNullReference(Instruction originalInstruction, int objectRegisterNumber) {
|
||||
super(originalInstruction.opcode);
|
||||
this.OriginalInstruction = originalInstruction;
|
||||
this.ObjectRegisterNum = objectRegisterNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return OriginalInstruction.getSize();
|
||||
}
|
||||
|
||||
public Format getFormat() {
|
||||
return Format.Format35msn;
|
||||
return Format.UnresolvedNullReference;
|
||||
}
|
||||
}
|
@ -205,7 +205,9 @@ public class DeodexUtil {
|
||||
if (deodexInstruction(i)) {
|
||||
didSomething = true;
|
||||
} else {
|
||||
somethingLeftToDo = true;
|
||||
if (!i.dead) {
|
||||
somethingLeftToDo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,7 +221,13 @@ public class DeodexUtil {
|
||||
|
||||
List<Instruction> instructions = new ArrayList<Instruction>(insns.size());
|
||||
for (insn i: insns) {
|
||||
if (i.instruction.opcode.odexOnly) {
|
||||
if (i.dead) {
|
||||
if (i.fixedInstruction != null) {
|
||||
instructions.add(new DeadInstruction(i.fixedInstruction));
|
||||
} else {
|
||||
instructions.add(new DeadInstruction(i.instruction));
|
||||
}
|
||||
} else if (i.instruction.opcode.odexOnly) {
|
||||
assert i.fixedInstruction != null;
|
||||
instructions.add(i.fixedInstruction);
|
||||
} else {
|
||||
@ -289,7 +297,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -351,7 +359,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,7 +400,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -435,7 +443,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -497,7 +505,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -539,7 +547,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction2csn, which doesn't have any method/field info associated with
|
||||
//it, and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction22csn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -581,7 +589,8 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction3msn, which doesn't have any method info associated with it,
|
||||
//and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction35msn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
i.propogateDeadness();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -627,7 +636,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction3msn, which doesn't have any method info associated with it,
|
||||
//and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction35msn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -673,7 +682,9 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction3msn, which doesn't have any method info associated with it,
|
||||
//and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction35msn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
//we need to mark any following instructions as dead
|
||||
i.propogateDeadness();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -719,7 +730,7 @@ public class DeodexUtil {
|
||||
//We actually just create an Instruction3msn, which doesn't have any method info associated with it,
|
||||
//and let the caller choose which "default" method to call in this case
|
||||
if (regType == RegisterType.Null) {
|
||||
i.fixedInstruction = new Instruction35msn(registerNum);
|
||||
i.fixedInstruction = new UnresolvedNullReference(i.instruction, registerNum);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -804,6 +815,11 @@ public class DeodexUtil {
|
||||
*/
|
||||
public LinkedList<insn> successors = new LinkedList<insn>();
|
||||
|
||||
/**
|
||||
* Instructions that can pass on execution to this one
|
||||
*/
|
||||
public LinkedList<insn> predecessors = new LinkedList<insn>();
|
||||
|
||||
/**
|
||||
* If this instruction is in a try block, these are the first instructions for each
|
||||
* exception handler
|
||||
@ -860,6 +876,12 @@ public class DeodexUtil {
|
||||
*/
|
||||
public boolean fixed = false;
|
||||
|
||||
/**
|
||||
* If this code is dead. Note that not all dead code is marked. Only the dead code that comes after an odexed
|
||||
* instruction can't be resolved because its object register is always null.
|
||||
*/
|
||||
public boolean dead = false;
|
||||
|
||||
public final RegisterType[] registerMap;
|
||||
public final String[] registerTypes;
|
||||
|
||||
@ -1246,6 +1268,7 @@ public class DeodexUtil {
|
||||
|
||||
private void addSuccessor(insn i) {
|
||||
successors.add(i);
|
||||
i.predecessors.add(this);
|
||||
|
||||
//if the next instruction can throw an exception, and is covered by exception handlers,
|
||||
//then the execution can in effect go directly from this instruction into the handler
|
||||
@ -1332,6 +1355,38 @@ public class DeodexUtil {
|
||||
this.propogateRegisters();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is initially called when this instruction is an odexed instruction that can't be resolved because
|
||||
* the object register is always null. For each of its successor instructions, it checks if all of the other
|
||||
* predecessors of that successor are also "dead". If so, it marks it as dead and recursively calls
|
||||
* propogateDeadness on it. The effect is that all the code that follows after the initial unresolved
|
||||
* instruction is marked dead, until a non-dead code path merges with the dead code path (and it becomes...
|
||||
* undead. mmMMmmMMmm brains)
|
||||
*/
|
||||
public void propogateDeadness() {
|
||||
for (insn successor: successors) {
|
||||
//the first instruction of the method (or the first instruction of any exception handlers covering
|
||||
//the first instruction) can never be dead
|
||||
if (successor.firstInstruction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean allSucessorsPredecessorsDead = true;
|
||||
for (insn successorPredecessor: successor.predecessors) {
|
||||
if (successorPredecessor != this) {
|
||||
if (!successorPredecessor.dead) {
|
||||
allSucessorsPredecessorsDead = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allSucessorsPredecessorsDead) {
|
||||
successor.dead = true;
|
||||
successor.propogateDeadness();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void propogateRegisters() {
|
||||
visited = true;
|
||||
|
||||
@ -1399,7 +1454,6 @@ public class DeodexUtil {
|
||||
}
|
||||
} else {
|
||||
String type = destRegisterType();
|
||||
String nextType = nextInsn.registerTypes[registerNum];
|
||||
|
||||
if (type != null && !type.equals(nextInsn.registerTypes[registerNum])) {
|
||||
//see comment above for loop
|
||||
|
Reference in New Issue
Block a user