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

@ -46,18 +46,16 @@ public class CatchMethodItem extends MethodItem {
this.exceptionType = exceptionType;
tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_"));
tryStartLabel.setUncommented();
//use the address from the last covered instruction, but make the label
//name refer to the address of the next instruction
tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress));
tryEndLabel.setUncommented();
if (exceptionType == null) {
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_"));
} else {
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_"));
}
handlerLabel.setUncommented();
}
public LabelMethodItem getTryStartLabel() {

View File

@ -37,55 +37,30 @@ import java.io.IOException;
import java.util.Iterator;
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayDataPseudoInstruction> {
private final boolean dead;
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, boolean dead,
ArrayDataPseudoInstruction instruction) {
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) {
super(codeItem, codeAddress, instruction);
this.dead = dead;
}
public boolean writeTo(IndentingWriter writer) throws IOException {
if (dead) {
writer.write("#.array-data 0x");
writer.printLongAsHex(instruction.getElementWidth());
writer.write('\n');
writer.write(".array-data 0x");
writer.printLongAsHex(instruction.getElementWidth());
writer.write('\n');
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
while (iterator.hasNext()) {
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
writer.indent(4);
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
while (iterator.hasNext()) {
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
writer.write("# ");
for (int i=0; i<element.elementWidth; i++) {
if (i!=0) {
writer.write(' ');
}
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
for (int i=0; i<element.elementWidth; i++) {
if (i!=0) {
writer.write(' ');
}
writer.write('\n');
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
}
writer.write("#.end array-data");
} else {
writer.write(".array-data 0x");
writer.printLongAsHex(instruction.getElementWidth());
writer.write('\n');
writer.indent(4);
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
while (iterator.hasNext()) {
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
for (int i=0; i<element.elementWidth; i++) {
if (i!=0) {
writer.write(' ');
}
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
}
writer.write('\n');
}
writer.deindent(4);
writer.write(".end array-data");
}
writer.deindent(4);
writer.write(".end array-data");
return true;
}
}

View File

@ -29,10 +29,7 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.baksmali.Adaptors.MethodDefinition;
import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction;
import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction;
import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction;
import org.jf.dexlib.Code.Format.UnresolvedNullReference;
import org.jf.dexlib.Code.Format.*;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.OffsetInstruction;
import org.jf.dexlib.CodeItem;
@ -52,46 +49,17 @@ public class InstructionMethodItemFactory {
switch (instruction.getFormat()) {
case ArrayData:
return new ArrayDataMethodItem(codeItem, codeAddress, false,
return new ArrayDataMethodItem(codeItem, codeAddress,
(ArrayDataPseudoInstruction)instruction);
case PackedSwitchData:
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress, false,
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress,
(PackedSwitchDataPseudoInstruction)instruction);
case SparseSwitchData:
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress, false,
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress,
(SparseSwitchDataPseudoInstruction)instruction);
case UnresolvedNullReference:
assert false;
throw new RuntimeException("UnresolvedNullReference not supported, use " +
"makeAnalyzedInstructionFormatMethodItem instead");
default:
return new InstructionMethodItem(codeItem, codeAddress, instruction);
}
}
public static InstructionMethodItem makeAnalyzedInstructionFormatMethodItem(MethodDefinition methodDefinition,
CodeItem codeItem, int codeAddress,
boolean isDead,
Instruction instruction,
boolean isLastInstruction) {
if (instruction instanceof OffsetInstruction) {
return new OffsetInstructionFormatMethodItem(methodDefinition.getLabelCache(), codeItem, codeAddress,
instruction);
}
switch (instruction.getFormat()) {
case ArrayData:
return new ArrayDataMethodItem(codeItem, codeAddress, isDead,
(ArrayDataPseudoInstruction)instruction);
case PackedSwitchData:
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress, isDead,
(PackedSwitchDataPseudoInstruction)instruction);
case SparseSwitchData:
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress, isDead,
(SparseSwitchDataPseudoInstruction)instruction);
case UnresolvedNullReference:
return new UnresolvedNullReferenceMethodItem(codeItem, codeAddress,
(UnresolvedNullReference)instruction, isLastInstruction);
case UnresolvedOdexInstruction:
return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress,
(UnresolvedOdexInstruction)instruction);
default:
return new InstructionMethodItem(codeItem, codeAddress, instruction);
}

View File

@ -48,7 +48,6 @@ public class OffsetInstructionFormatMethodItem<T extends Instruction & OffsetIns
label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix());
label = labelCache.internLabel(label);
label.setUncommented();
}
@Override

View File

@ -43,9 +43,8 @@ import java.util.List;
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDataPseudoInstruction>
implements Iterable<LabelMethodItem> {
private final List<LabelMethodItem> labels;
private final boolean dead;
public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, boolean dead,
public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
PackedSwitchDataPseudoInstruction instruction) {
super(codeItem, codeAddress, instruction);
@ -58,36 +57,21 @@ public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDa
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_");
label = methodDefinition.getLabelCache().internLabel(label);
labels.add(label);
label.setUncommented();
}
this.dead = dead;
}
@Override
public boolean writeTo(IndentingWriter writer) throws IOException {
if (dead) {
writer.write("#.packed-switch ");
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
writer.write(".packed-switch ");
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
writer.indent(4);
writer.write('\n');
for (LabelMethodItem label: labels) {
label.writeTo(writer);
writer.write('\n');
for (LabelMethodItem label: labels) {
writer.write("# ");
label.writeTo(writer);
writer.write('\n');
}
writer.write("#.end packed-switch");
} else {
writer.write(".packed-switch ");
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
writer.indent(4);
writer.write('\n');
for (LabelMethodItem label: labels) {
label.writeTo(writer);
writer.write('\n');
}
writer.deindent(4);
writer.write(".end packed-switch");
}
writer.deindent(4);
writer.write(".end packed-switch");
return true;
}

View File

@ -43,9 +43,8 @@ import java.util.List;
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDataPseudoInstruction>
implements Iterable<LabelMethodItem> {
private final List<SparseSwitchTarget> targets;
private final boolean dead;
public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, boolean dead,
public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
SparseSwitchDataPseudoInstruction instruction) {
super(codeItem, codeAddress, instruction);
@ -61,37 +60,23 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_");
label = methodDefinition.getLabelCache().internLabel(label);
sparseSwitchTarget.Target = label;
label.setUncommented();
targets.add(sparseSwitchTarget);
}
this.dead = dead;
}
@Override
public boolean writeTo(IndentingWriter writer) throws IOException {
if (dead) {
writer.write("#.sparse-switch\n");
for (SparseSwitchTarget target: targets) {
IntegerRenderer.writeTo(writer, target.Key);
writer.write(" -> ");
target.Target.writeTo(writer);
writer.write('\n');
}
writer.write("#.end sparse-switch");
} else {
writer.write(".sparse-switch\n");
writer.indent(4);
for (SparseSwitchTarget target: targets) {
IntegerRenderer.writeTo(writer, target.Key);
writer.write(" -> ");
target.Target.writeTo(writer);
writer.write('\n');
}
writer.deindent(4);
writer.write(".end sparse-switch");
writer.write(".sparse-switch\n");
writer.indent(4);
for (SparseSwitchTarget target: targets) {
IntegerRenderer.writeTo(writer, target.Key);
writer.write(" -> ");
target.Target.writeTo(writer);
writer.write('\n');
}
writer.deindent(4);
writer.write(".end sparse-switch");
return true;
}

View File

@ -29,49 +29,23 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.baksmali.IndentingWriter;
import org.jf.dexlib.Code.Format.UnresolvedNullReference;
import org.jf.dexlib.Code.Format.UnresolvedOdexInstruction;
import org.jf.dexlib.CodeItem;
import java.io.IOException;
public class UnresolvedNullReferenceMethodItem extends InstructionMethodItem<UnresolvedNullReference> {
public final boolean isLastInstruction;
public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int codeAddress, UnresolvedNullReference instruction,
boolean isLastInstruction) {
public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<UnresolvedOdexInstruction> {
public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) {
super(codeItem, codeAddress, instruction);
this.isLastInstruction = isLastInstruction;
}
public boolean writeTo(IndentingWriter writer) throws IOException {
switch (instruction.OriginalInstruction.opcode)
{
case INVOKE_VIRTUAL_QUICK_RANGE:
case INVOKE_SUPER_QUICK_RANGE:
writeInvokeRangeTo(writer);
return true;
default:
writeThrowTo(writer);
return true;
}
}
private void writeInvokeRangeTo(IndentingWriter writer) throws IOException {
writer.write("#Replaced unresolvable optimized invoke-*-range-quick instruction\n");
writer.write("#with a generic method call that will throw a NullPointerException\n");
writer.write("invoke-virtual/range {");
writeRegister(writer, instruction.ObjectRegisterNum);
writer.write(" .. ");
writeRegister(writer, instruction.ObjectRegisterNum);
writer.write("}, Ljava/lang/Object;->hashCode()I");
if (isLastInstruction) {
writer.write('\n');
writer.write("goto/32 0");
}
writeThrowTo(writer);
return true;
}
private void writeThrowTo(IndentingWriter writer) throws IOException {
writer.write("#Replaced unresolvable optimized instruction with a throw\n");
writer.write("#Replaced unresolvable odex instruction with a throw\n");
writer.write("throw ");
writeRegister(writer, instruction.ObjectRegisterNum);
}

View File

@ -36,7 +36,6 @@ import java.io.IOException;
public class LabelMethodItem extends MethodItem {
private final String labelPrefix;
private int labelSequence;
private boolean isCommentedOut = true;
public LabelMethodItem(int codeAddress, String labelPrefix) {
super(codeAddress);
@ -47,14 +46,6 @@ public class LabelMethodItem extends MethodItem {
return 0;
}
public boolean isCommentedOut() {
return isCommentedOut;
}
public void setUncommented() {
this.isCommentedOut = false;
}
public int compareTo(MethodItem methodItem) {
int result = super.compareTo(methodItem);

View File

@ -300,11 +300,7 @@ public class MethodDefinition {
}
for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
if (labelMethodItem.isCommentedOut()) {
methodItems.add(new CommentedOutMethodItem(labelMethodItem));
} else {
methodItems.add(labelMethodItem);
}
methodItems.add(labelMethodItem);
}
Collections.sort(methodItems);
@ -372,51 +368,19 @@ public class MethodDefinition {
List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
AnalyzedInstruction lastInstruction = null;
for (int i=instructions.size()-1; i>=0; i--) {
AnalyzedInstruction instruction = instructions.get(i);
if (!instruction.isDead()) {
lastInstruction = instruction;
break;
}
}
boolean lastIsUnreachable = false;
int currentCodeAddress = 0;
for (int i=0; i<instructions.size(); i++) {
AnalyzedInstruction instruction = instructions.get(i);
MethodItem methodItem = InstructionMethodItemFactory.makeAnalyzedInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, instruction.isDead(), instruction.getInstruction(),
instruction == lastInstruction);
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, instruction.getInstruction());
if (instruction.isDead() && !instruction.getInstruction().getFormat().variableSizeFormat) {
methodItems.add(new CommentedOutMethodItem(methodItem));
lastIsUnreachable = false;
} else if ( instruction.getPredecessorCount() == 0 &&
!instruction.getInstruction().getFormat().variableSizeFormat &&
!isInstructionPaddingNop(instructions, instruction)) {
methodItems.add(methodItem);
if (!lastIsUnreachable) {
methodItems.add(
new CommentMethodItem("Unreachable code", currentCodeAddress, Double.MIN_VALUE));
}
methodItems.add(new CommentedOutMethodItem(methodItem));
lastIsUnreachable = true;
} else {
methodItems.add(methodItem);
lastIsUnreachable = false;
}
if (instruction.getInstruction().getFormat() == Format.UnresolvedNullReference) {
if (instruction.getInstruction().getFormat() == Format.UnresolvedOdexInstruction) {
methodItems.add(new CommentedOutMethodItem(
InstructionMethodItemFactory.makeAnalyzedInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, instruction.isDead(),
instruction.getOriginalInstruction(), false)));
InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, instruction.getOriginalInstruction())));
}
if (i != instructions.size() - 1) {
@ -651,9 +615,6 @@ public class MethodDefinition {
public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
if (internedLabelMethodItem != null) {
if (!labelMethodItem.isCommentedOut()) {
internedLabelMethodItem.setUncommented();
}
return internedLabelMethodItem;
}
labels.put(labelMethodItem, labelMethodItem);

View File

@ -13,129 +13,37 @@
new-instance v2, Lsuperclass;
invoke-direct {v2}, Lsuperclass;-><init>()V
if-eqz v0, :here2
if-eqz v0, :here
#this is the unresolvable instruction. v0 is always null,
#and this will always throw an exception. Everything below
#here, until the here2: label is dead code, and should be
#commented out. This instruction itself should be be replaced
#with a call to Ljava/lang/Object;->hashCode()I
#and this will always throw an exception. It should be
#replaced with throw v0.
invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass;
move-result-object v1
#a branch to outside the dead code. The branch label should not
#be commented out, because there is a non-dead instruction
#that branches to it
if-eqz v0, :here2
#a branch to inside the dead code. the branch label should be
#commented out
if-eqz v0, :here
#another branch to outside the dead code. In this case, the "dead"
#instruction is the first instruction that references the label.
#the label should not be commented out, because it is referenced
#be a non-dead instruction
if-eqz v0, :here3
#one more branch to out the dead code. the branch label should be
#commented out, because there are no other non-dead instructions
#referenceding it
if-eqz v0, :here4
#this would normally be deodexable, except that it follows
#the above un-deodexeable instruction, which prevents the
#propagation of any register information. It can never be
#reached, and should be replaced with throw v2
invoke-virtual {v2}, Lsuperclass;->somemethod()V
#another odexed instruction that uses the result of the
#first unresolveable odex instruction. this should
#appear as a commented invoke-virtual-quick instruction
#first unresolveable odex instruction. This should
#be replaced with throw v1
invoke-virtual {v1}, Lsuperclass;->somemethod()V
:here
#a resolveable odex instruction in the dead code. It should be resolved,
#but still commented out
invoke-virtual {v2}, Lsuperclass;->somemethod()V
const v0, 1
new-array v0, v0, [I
#this should be marked as dead, and so should the corresponding .sparse-switch structure
fill-array-data v0, :array-data
const v0, 0
#this should be marked as dead, and so should the corresponding .sparse-switch structure
sparse-switch v0, :sparse-switch
#this should be marked as dead, and so should the corresponding .packed-switch structure
packed-switch v0, :packed-switch
:here2
#and we're back to the non-dead code
invoke-virtual {v2}, Lsuperclass;->somemethod()V
if-nez v0, :here3
return-void
#this should be marked as dead
:packed-switch
.packed-switch 0x0
:here
.end packed-switch
#this should be marked as dead
:sparse-switch
.sparse-switch
0x0 -> :here
.end sparse-switch
#this should be marked as dead
:array-data
.array-data 0x4
0x0t 0x0t 0x0t 0x0t
.end array-data
.end method
.method public static UnresolvedInstructionTest1()Lsuperclass;
.registers 2
const v0, 0
#this is an unresolvable instruction, due to v0 always being null
#this instruction should be replaced with "throw v0", followed by
#a "goto/32 0", since it would otherwise be the last instruction
#in the method, which isn't allowed
invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass;
#the following instructions should be commented out
move-result-object v1
return-object v1
.end method
.method public static UnresolvedInstructionTest2()Lsuperclass;
.registers 2
const v0, 0
if-eqz v0, :here
#this is an unresolvable instruction, due to v0 always being null
#this instruction should be replaced with "throw v0". There shouldn't
#be a "goto/32 0" afterwards, since it won't be the last instruction
#in the method.
invoke-virtual/range {v0 .. v0}, Lrandomclass;->getSuperclass()Lsuperclass;
#the following instructions should be commented out
move-result-object v1
return-object v1
#and now back to our normal programming
:here
return-object v0
.end method
.method public static FirstInstructionTest(Lrandomclass;)V

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;
}
}