Change the way unresolvable odex instructions are handled, so that it doesn't cause problems with try/catch blocks

git-svn-id: https://smali.googlecode.com/svn/trunk@737 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com
2010-06-12 15:55:44 +00:00
parent f7344d33d0
commit e01409c11f
14 changed files with 97 additions and 402 deletions

View File

@ -73,24 +73,10 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
*/
protected final Instruction originalInstruction;
/**
* A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed
* because it's object register is always null. In the non-odexed code that the odex was generated from, we would
* have technically considered this code reachable and could verify it, even though the instruction that ended up
* being odexed was always null, because we would assume both "paths" out of the instruction are valid - the one
* where execution proceeds normally to the next instruction, and the one where an exception occurs and execution
* either goes to a catch block, or out of the method.
*
* However, in the odexed case, we can't verify the code following an undeodexable instruction because we lack
* the register information from the undeodexable instruction - because we don't know the actual method or field
* that is being accessed.
*
* The undeodexable instruction is guaranteed to throw an NPE, so the following code is effectivetly unreachable.
* Once we detect an undeodexeable instruction, the following code is marked as dead up until a non-dead execution
* path merges in. Additionally, we remove the predecessors/successors of any dead instruction. For example, if
* there is a dead goto instruction, then we would remove the target instruction as a successor, and we would
* also remove the dead goto instruction as a predecessor to the target.
* An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction
* is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no
* code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed.
*/
protected boolean dead = false;

View File

@ -184,7 +184,7 @@ public class MethodAnalyzer {
instructionsToAnalyze.set(successor.instructionIndex);
}
BitSet odexedInstructions = new BitSet(instructions.size());
BitSet undeodexedInstructions = new BitSet(instructions.size());
do {
boolean didSomething = false;
@ -199,15 +199,18 @@ public class MethodAnalyzer {
instructionToAnalyze.dead = false;
try {
if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) {
//if we had deodexed an odex instruction in a previous pass, we might have more specific
//register information now, so let's restore the original odexed instruction and
//re-deodex it
instructionToAnalyze.restoreOdexedInstruction();
}
if (!analyzeInstruction(instructionToAnalyze)) {
odexedInstructions.set(i);
undeodexedInstructions.set(i);
continue;
} else {
didSomething = true;
odexedInstructions.clear(i);
undeodexedInstructions.clear(i);
}
} catch (ValidationException ex) {
this.validationException = ex;
@ -234,32 +237,32 @@ public class MethodAnalyzer {
break;
}
if (!odexedInstructions.isEmpty()) {
for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) {
if (!undeodexedInstructions.isEmpty()) {
for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) {
instructionsToAnalyze.set(i);
}
}
} while (true);
for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) {
for (int i=0; i<instructions.size(); i++) {
AnalyzedInstruction instruction = instructions.valueAt(i);
Instruction odexedInstruction = instruction.instruction;
int objectRegisterNumber;
if (odexedInstruction.getFormat() == Format.Format22cs) {
objectRegisterNumber = ((Instruction22cs)odexedInstruction).getRegisterB();
} else if (odexedInstruction.getFormat() == Format.Format35ms) {
objectRegisterNumber = ((Instruction35ms)odexedInstruction).getRegisterD();
} else if (odexedInstruction.getFormat() == Format.Format3rms) {
objectRegisterNumber = ((Instruction3rms)odexedInstruction).getStartRegister();
} else {
assert false;
throw new ExceptionWithContext(String.format("Unexpected format %s for odexed instruction",
odexedInstruction.getFormat().name()));
switch (instruction.getInstruction().getFormat()) {
case Format22cs:
objectRegisterNumber = ((Instruction22cs)instruction.instruction).getRegisterB();
break;
case Format35ms:
objectRegisterNumber = ((Instruction35ms)instruction.instruction).getRegisterD();
break;
case Format3rms:
objectRegisterNumber = ((Instruction3rms)instruction.instruction).getStartRegister();
break;
default:
continue;
}
instruction.setDeodexedInstruction(new UnresolvedNullReference(odexedInstruction,
instruction.setDeodexedInstruction(new UnresolvedOdexInstruction(instruction.instruction,
objectRegisterNumber));
}
@ -394,47 +397,6 @@ public class MethodAnalyzer {
registerType);
}
private void setAndPropagateDeadness(AnalyzedInstruction analyzedInstruction) {
BitSet instructionsToProcess = new BitSet(instructions.size());
analyzedInstruction.dead = true;
for (AnalyzedInstruction successor: analyzedInstruction.successors) {
instructionsToProcess.set(successor.instructionIndex);
}
instructionsToProcess.set(analyzedInstruction.instructionIndex);
while (!instructionsToProcess.isEmpty()) {
for (int i=instructionsToProcess.nextSetBit(0); i>=0; i=instructionsToProcess.nextSetBit(i+1)) {
AnalyzedInstruction currentInstruction = instructions.valueAt(i);
instructionsToProcess.clear(i);
if (currentInstruction.dead) {
continue;
}
boolean isDead = true;
for (AnalyzedInstruction predecessor: currentInstruction.predecessors) {
if (!predecessor.dead) {
isDead = false;
break;
}
}
if (isDead) {
currentInstruction.dead = true;
for (AnalyzedInstruction successor: currentInstruction.successors) {
instructionsToProcess.set(successor.instructionIndex);
}
}
}
}
}
private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber,
RegisterType registerType) {
@ -658,6 +620,10 @@ public class MethodAnalyzer {
return exceptionHandlers;
}
/**
* @return false if analyzedInstruction is an odex instruction that couldn't be deodexed, due to its
* object register being null
*/
private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) {
Instruction instruction = analyzedInstruction.instruction;

View File

@ -62,7 +62,7 @@ public enum Format {
ArrayData(null, -1, true),
PackedSwitchData(null, -1, true),
SparseSwitchData(null, -1, true),
UnresolvedNullReference(null, -1, false),
UnresolvedOdexInstruction(null, -1, false),
;
public final Instruction.InstructionFactory Factory;

View File

@ -36,12 +36,12 @@ import org.jf.dexlib.Util.AnnotatedOutput;
* can't be determined. Typically, these are replaced by an equivalent instruction that would have the same
* effect (namely, an NPE)
*/
public class UnresolvedNullReference extends Instruction {
public class UnresolvedOdexInstruction extends Instruction {
public final Instruction OriginalInstruction;
//the register number that holds the (null) reference type that the instruction operates on
public final int ObjectRegisterNum;
public UnresolvedNullReference(Instruction originalInstruction, int objectRegisterNumber) {
public UnresolvedOdexInstruction(Instruction originalInstruction, int objectRegisterNumber) {
super(originalInstruction.opcode);
this.OriginalInstruction = originalInstruction;
this.ObjectRegisterNum = objectRegisterNumber;
@ -57,6 +57,6 @@ public class UnresolvedNullReference extends Instruction {
}
public Format getFormat() {
return Format.UnresolvedNullReference;
return Format.UnresolvedOdexInstruction;
}
}