mirror of
https://github.com/revanced/smali.git
synced 2025-06-12 04:17:36 +02:00
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:
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user