mirror of
https://github.com/revanced/smali.git
synced 2025-05-06 17:34:34 +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:
parent
f7344d33d0
commit
e01409c11f
@ -46,18 +46,16 @@ public class CatchMethodItem extends MethodItem {
|
|||||||
this.exceptionType = exceptionType;
|
this.exceptionType = exceptionType;
|
||||||
|
|
||||||
tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_"));
|
tryStartLabel = labelCache.internLabel(new LabelMethodItem(startAddress, "try_start_"));
|
||||||
tryStartLabel.setUncommented();
|
|
||||||
//use the address from the last covered instruction, but make the label
|
//use the address from the last covered instruction, but make the label
|
||||||
//name refer to the address of the next instruction
|
//name refer to the address of the next instruction
|
||||||
tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress));
|
tryEndLabel = labelCache.internLabel(new EndTryLabelMethodItem(codeAddress, endAddress));
|
||||||
tryEndLabel.setUncommented();
|
|
||||||
|
|
||||||
if (exceptionType == null) {
|
if (exceptionType == null) {
|
||||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_"));
|
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catchall_"));
|
||||||
} else {
|
} else {
|
||||||
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_"));
|
handlerLabel = labelCache.internLabel(new LabelMethodItem(handlerAddress, "catch_"));
|
||||||
}
|
}
|
||||||
handlerLabel.setUncommented();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LabelMethodItem getTryStartLabel() {
|
public LabelMethodItem getTryStartLabel() {
|
||||||
|
@ -37,55 +37,30 @@ import java.io.IOException;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayDataPseudoInstruction> {
|
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayDataPseudoInstruction> {
|
||||||
private final boolean dead;
|
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) {
|
||||||
|
|
||||||
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, boolean dead,
|
|
||||||
ArrayDataPseudoInstruction instruction) {
|
|
||||||
super(codeItem, codeAddress, instruction);
|
super(codeItem, codeAddress, instruction);
|
||||||
this.dead = dead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
if (dead) {
|
writer.write(".array-data 0x");
|
||||||
writer.write("#.array-data 0x");
|
writer.printLongAsHex(instruction.getElementWidth());
|
||||||
writer.printLongAsHex(instruction.getElementWidth());
|
writer.write('\n');
|
||||||
writer.write('\n');
|
|
||||||
|
|
||||||
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
|
writer.indent(4);
|
||||||
while (iterator.hasNext()) {
|
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
|
||||||
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
|
while (iterator.hasNext()) {
|
||||||
|
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
|
||||||
|
|
||||||
writer.write("# ");
|
for (int i=0; i<element.elementWidth; i++) {
|
||||||
for (int i=0; i<element.elementWidth; i++) {
|
if (i!=0) {
|
||||||
if (i!=0) {
|
writer.write(' ');
|
||||||
writer.write(' ');
|
|
||||||
}
|
|
||||||
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
|
|
||||||
}
|
}
|
||||||
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.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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,7 @@
|
|||||||
package org.jf.baksmali.Adaptors.Format;
|
package org.jf.baksmali.Adaptors.Format;
|
||||||
|
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction;
|
import org.jf.dexlib.Code.Format.*;
|
||||||
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.Instruction;
|
import org.jf.dexlib.Code.Instruction;
|
||||||
import org.jf.dexlib.Code.OffsetInstruction;
|
import org.jf.dexlib.Code.OffsetInstruction;
|
||||||
import org.jf.dexlib.CodeItem;
|
import org.jf.dexlib.CodeItem;
|
||||||
@ -52,46 +49,17 @@ public class InstructionMethodItemFactory {
|
|||||||
|
|
||||||
switch (instruction.getFormat()) {
|
switch (instruction.getFormat()) {
|
||||||
case ArrayData:
|
case ArrayData:
|
||||||
return new ArrayDataMethodItem(codeItem, codeAddress, false,
|
return new ArrayDataMethodItem(codeItem, codeAddress,
|
||||||
(ArrayDataPseudoInstruction)instruction);
|
(ArrayDataPseudoInstruction)instruction);
|
||||||
case PackedSwitchData:
|
case PackedSwitchData:
|
||||||
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress, false,
|
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
||||||
(PackedSwitchDataPseudoInstruction)instruction);
|
(PackedSwitchDataPseudoInstruction)instruction);
|
||||||
case SparseSwitchData:
|
case SparseSwitchData:
|
||||||
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress, false,
|
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
||||||
(SparseSwitchDataPseudoInstruction)instruction);
|
(SparseSwitchDataPseudoInstruction)instruction);
|
||||||
case UnresolvedNullReference:
|
case UnresolvedOdexInstruction:
|
||||||
assert false;
|
return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress,
|
||||||
throw new RuntimeException("UnresolvedNullReference not supported, use " +
|
(UnresolvedOdexInstruction)instruction);
|
||||||
"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);
|
|
||||||
default:
|
default:
|
||||||
return new InstructionMethodItem(codeItem, codeAddress, instruction);
|
return new InstructionMethodItem(codeItem, codeAddress, instruction);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,6 @@ public class OffsetInstructionFormatMethodItem<T extends Instruction & OffsetIns
|
|||||||
|
|
||||||
label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix());
|
label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix());
|
||||||
label = labelCache.internLabel(label);
|
label = labelCache.internLabel(label);
|
||||||
label.setUncommented();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,9 +43,8 @@ import java.util.List;
|
|||||||
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDataPseudoInstruction>
|
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDataPseudoInstruction>
|
||||||
implements Iterable<LabelMethodItem> {
|
implements Iterable<LabelMethodItem> {
|
||||||
private final List<LabelMethodItem> labels;
|
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) {
|
PackedSwitchDataPseudoInstruction instruction) {
|
||||||
super(codeItem, codeAddress, instruction);
|
super(codeItem, codeAddress, instruction);
|
||||||
|
|
||||||
@ -58,36 +57,21 @@ public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDa
|
|||||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_");
|
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_");
|
||||||
label = methodDefinition.getLabelCache().internLabel(label);
|
label = methodDefinition.getLabelCache().internLabel(label);
|
||||||
labels.add(label);
|
labels.add(label);
|
||||||
label.setUncommented();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dead = dead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
if (dead) {
|
writer.write(".packed-switch ");
|
||||||
writer.write("#.packed-switch ");
|
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
|
||||||
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
|
writer.indent(4);
|
||||||
|
writer.write('\n');
|
||||||
|
for (LabelMethodItem label: labels) {
|
||||||
|
label.writeTo(writer);
|
||||||
writer.write('\n');
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +43,8 @@ import java.util.List;
|
|||||||
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDataPseudoInstruction>
|
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDataPseudoInstruction>
|
||||||
implements Iterable<LabelMethodItem> {
|
implements Iterable<LabelMethodItem> {
|
||||||
private final List<SparseSwitchTarget> targets;
|
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) {
|
SparseSwitchDataPseudoInstruction instruction) {
|
||||||
super(codeItem, codeAddress, instruction);
|
super(codeItem, codeAddress, instruction);
|
||||||
|
|
||||||
@ -61,37 +60,23 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
|
|||||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_");
|
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_");
|
||||||
label = methodDefinition.getLabelCache().internLabel(label);
|
label = methodDefinition.getLabelCache().internLabel(label);
|
||||||
sparseSwitchTarget.Target = label;
|
sparseSwitchTarget.Target = label;
|
||||||
label.setUncommented();
|
|
||||||
|
|
||||||
targets.add(sparseSwitchTarget);
|
targets.add(sparseSwitchTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dead = dead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
if (dead) {
|
writer.write(".sparse-switch\n");
|
||||||
writer.write("#.sparse-switch\n");
|
writer.indent(4);
|
||||||
for (SparseSwitchTarget target: targets) {
|
for (SparseSwitchTarget target: targets) {
|
||||||
IntegerRenderer.writeTo(writer, target.Key);
|
IntegerRenderer.writeTo(writer, target.Key);
|
||||||
writer.write(" -> ");
|
writer.write(" -> ");
|
||||||
target.Target.writeTo(writer);
|
target.Target.writeTo(writer);
|
||||||
writer.write('\n');
|
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.deindent(4);
|
||||||
|
writer.write(".end sparse-switch");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,49 +29,23 @@
|
|||||||
package org.jf.baksmali.Adaptors.Format;
|
package org.jf.baksmali.Adaptors.Format;
|
||||||
|
|
||||||
import org.jf.baksmali.IndentingWriter;
|
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 org.jf.dexlib.CodeItem;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class UnresolvedNullReferenceMethodItem extends InstructionMethodItem<UnresolvedNullReference> {
|
public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<UnresolvedOdexInstruction> {
|
||||||
public final boolean isLastInstruction;
|
public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) {
|
||||||
|
|
||||||
public UnresolvedNullReferenceMethodItem(CodeItem codeItem, int codeAddress, UnresolvedNullReference instruction,
|
|
||||||
boolean isLastInstruction) {
|
|
||||||
super(codeItem, codeAddress, instruction);
|
super(codeItem, codeAddress, instruction);
|
||||||
this.isLastInstruction = isLastInstruction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
switch (instruction.OriginalInstruction.opcode)
|
writeThrowTo(writer);
|
||||||
{
|
return true;
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeThrowTo(IndentingWriter writer) throws IOException {
|
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 ");
|
writer.write("throw ");
|
||||||
writeRegister(writer, instruction.ObjectRegisterNum);
|
writeRegister(writer, instruction.ObjectRegisterNum);
|
||||||
}
|
}
|
@ -36,7 +36,6 @@ import java.io.IOException;
|
|||||||
public class LabelMethodItem extends MethodItem {
|
public class LabelMethodItem extends MethodItem {
|
||||||
private final String labelPrefix;
|
private final String labelPrefix;
|
||||||
private int labelSequence;
|
private int labelSequence;
|
||||||
private boolean isCommentedOut = true;
|
|
||||||
|
|
||||||
public LabelMethodItem(int codeAddress, String labelPrefix) {
|
public LabelMethodItem(int codeAddress, String labelPrefix) {
|
||||||
super(codeAddress);
|
super(codeAddress);
|
||||||
@ -47,14 +46,6 @@ public class LabelMethodItem extends MethodItem {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCommentedOut() {
|
|
||||||
return isCommentedOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUncommented() {
|
|
||||||
this.isCommentedOut = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(MethodItem methodItem) {
|
public int compareTo(MethodItem methodItem) {
|
||||||
int result = super.compareTo(methodItem);
|
int result = super.compareTo(methodItem);
|
||||||
|
|
||||||
|
@ -300,11 +300,7 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
|
for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
|
||||||
if (labelMethodItem.isCommentedOut()) {
|
methodItems.add(labelMethodItem);
|
||||||
methodItems.add(new CommentedOutMethodItem(labelMethodItem));
|
|
||||||
} else {
|
|
||||||
methodItems.add(labelMethodItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(methodItems);
|
Collections.sort(methodItems);
|
||||||
@ -372,51 +368,19 @@ public class MethodDefinition {
|
|||||||
|
|
||||||
List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
|
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;
|
int currentCodeAddress = 0;
|
||||||
for (int i=0; i<instructions.size(); i++) {
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
AnalyzedInstruction instruction = instructions.get(i);
|
AnalyzedInstruction instruction = instructions.get(i);
|
||||||
|
|
||||||
MethodItem methodItem = InstructionMethodItemFactory.makeAnalyzedInstructionFormatMethodItem(this,
|
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||||
encodedMethod.codeItem, currentCodeAddress, instruction.isDead(), instruction.getInstruction(),
|
encodedMethod.codeItem, currentCodeAddress, instruction.getInstruction());
|
||||||
instruction == lastInstruction);
|
|
||||||
|
|
||||||
if (instruction.isDead() && !instruction.getInstruction().getFormat().variableSizeFormat) {
|
methodItems.add(methodItem);
|
||||||
methodItems.add(new CommentedOutMethodItem(methodItem));
|
|
||||||
lastIsUnreachable = false;
|
|
||||||
} else if ( instruction.getPredecessorCount() == 0 &&
|
|
||||||
!instruction.getInstruction().getFormat().variableSizeFormat &&
|
|
||||||
!isInstructionPaddingNop(instructions, instruction)) {
|
|
||||||
|
|
||||||
if (!lastIsUnreachable) {
|
if (instruction.getInstruction().getFormat() == Format.UnresolvedOdexInstruction) {
|
||||||
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) {
|
|
||||||
methodItems.add(new CommentedOutMethodItem(
|
methodItems.add(new CommentedOutMethodItem(
|
||||||
InstructionMethodItemFactory.makeAnalyzedInstructionFormatMethodItem(this,
|
InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||||
encodedMethod.codeItem, currentCodeAddress, instruction.isDead(),
|
encodedMethod.codeItem, currentCodeAddress, instruction.getOriginalInstruction())));
|
||||||
instruction.getOriginalInstruction(), false)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != instructions.size() - 1) {
|
if (i != instructions.size() - 1) {
|
||||||
@ -651,9 +615,6 @@ public class MethodDefinition {
|
|||||||
public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
|
public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
|
||||||
LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
|
LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
|
||||||
if (internedLabelMethodItem != null) {
|
if (internedLabelMethodItem != null) {
|
||||||
if (!labelMethodItem.isCommentedOut()) {
|
|
||||||
internedLabelMethodItem.setUncommented();
|
|
||||||
}
|
|
||||||
return internedLabelMethodItem;
|
return internedLabelMethodItem;
|
||||||
}
|
}
|
||||||
labels.put(labelMethodItem, labelMethodItem);
|
labels.put(labelMethodItem, labelMethodItem);
|
||||||
|
@ -13,129 +13,37 @@
|
|||||||
new-instance v2, Lsuperclass;
|
new-instance v2, Lsuperclass;
|
||||||
invoke-direct {v2}, Lsuperclass;-><init>()V
|
invoke-direct {v2}, Lsuperclass;-><init>()V
|
||||||
|
|
||||||
if-eqz v0, :here2
|
if-eqz v0, :here
|
||||||
|
|
||||||
|
|
||||||
#this is the unresolvable instruction. v0 is always null,
|
#this is the unresolvable instruction. v0 is always null,
|
||||||
#and this will always throw an exception. Everything below
|
#and this will always throw an exception. It should be
|
||||||
#here, until the here2: label is dead code, and should be
|
#replaced with throw v0.
|
||||||
#commented out. This instruction itself should be be replaced
|
|
||||||
#with a call to Ljava/lang/Object;->hashCode()I
|
|
||||||
invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass;
|
invoke-virtual {v0}, Lrandomclass;->getSuperclass()Lsuperclass;
|
||||||
move-result-object v1
|
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
|
if-eqz v0, :here
|
||||||
|
|
||||||
#another branch to outside the dead code. In this case, the "dead"
|
#this would normally be deodexable, except that it follows
|
||||||
#instruction is the first instruction that references the label.
|
#the above un-deodexeable instruction, which prevents the
|
||||||
#the label should not be commented out, because it is referenced
|
#propagation of any register information. It can never be
|
||||||
#be a non-dead instruction
|
#reached, and should be replaced with throw v2
|
||||||
if-eqz v0, :here3
|
invoke-virtual {v2}, Lsuperclass;->somemethod()V
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
#another odexed instruction that uses the result of the
|
#another odexed instruction that uses the result of the
|
||||||
#first unresolveable odex instruction. this should
|
#first unresolveable odex instruction. This should
|
||||||
#appear as a commented invoke-virtual-quick instruction
|
#be replaced with throw v1
|
||||||
invoke-virtual {v1}, Lsuperclass;->somemethod()V
|
invoke-virtual {v1}, Lsuperclass;->somemethod()V
|
||||||
|
|
||||||
:here
|
: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
|
#and we're back to the non-dead code
|
||||||
invoke-virtual {v2}, Lsuperclass;->somemethod()V
|
invoke-virtual {v2}, Lsuperclass;->somemethod()V
|
||||||
|
|
||||||
if-nez v0, :here3
|
if-nez v0, :here3
|
||||||
|
|
||||||
|
|
||||||
return-void
|
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
|
.end method
|
||||||
|
|
||||||
.method public static FirstInstructionTest(Lrandomclass;)V
|
.method public static FirstInstructionTest(Lrandomclass;)V
|
||||||
|
@ -73,24 +73,10 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
|||||||
*/
|
*/
|
||||||
protected final Instruction originalInstruction;
|
protected final Instruction originalInstruction;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dead instruction is one that is unreachable because it follows an odexed instruction that can't be deodexed
|
* An analyzed instruction's "deadness" is set during analysis (i.e. MethodAnalyzer.analyzer()). A dead instruction
|
||||||
* because it's object register is always null. In the non-odexed code that the odex was generated from, we would
|
* is one that the analyzer never reaches. This occurs either with natural "dead code" - code that simply has no
|
||||||
* have technically considered this code reachable and could verify it, even though the instruction that ended up
|
* code path that can ever reach it, or code that follows an odexed instruction that can't be deodexed.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
protected boolean dead = false;
|
protected boolean dead = false;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ public class MethodAnalyzer {
|
|||||||
instructionsToAnalyze.set(successor.instructionIndex);
|
instructionsToAnalyze.set(successor.instructionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
BitSet odexedInstructions = new BitSet(instructions.size());
|
BitSet undeodexedInstructions = new BitSet(instructions.size());
|
||||||
|
|
||||||
do {
|
do {
|
||||||
boolean didSomething = false;
|
boolean didSomething = false;
|
||||||
@ -199,15 +199,18 @@ public class MethodAnalyzer {
|
|||||||
instructionToAnalyze.dead = false;
|
instructionToAnalyze.dead = false;
|
||||||
try {
|
try {
|
||||||
if (instructionToAnalyze.originalInstruction.opcode.odexOnly()) {
|
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();
|
instructionToAnalyze.restoreOdexedInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!analyzeInstruction(instructionToAnalyze)) {
|
if (!analyzeInstruction(instructionToAnalyze)) {
|
||||||
odexedInstructions.set(i);
|
undeodexedInstructions.set(i);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
didSomething = true;
|
didSomething = true;
|
||||||
odexedInstructions.clear(i);
|
undeodexedInstructions.clear(i);
|
||||||
}
|
}
|
||||||
} catch (ValidationException ex) {
|
} catch (ValidationException ex) {
|
||||||
this.validationException = ex;
|
this.validationException = ex;
|
||||||
@ -234,32 +237,32 @@ public class MethodAnalyzer {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!odexedInstructions.isEmpty()) {
|
if (!undeodexedInstructions.isEmpty()) {
|
||||||
for (int i=odexedInstructions.nextSetBit(0); i>=0; i=odexedInstructions.nextSetBit(i+1)) {
|
for (int i=undeodexedInstructions.nextSetBit(0); i>=0; i=undeodexedInstructions.nextSetBit(i+1)) {
|
||||||
instructionsToAnalyze.set(i);
|
instructionsToAnalyze.set(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (true);
|
} 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);
|
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||||
|
|
||||||
Instruction odexedInstruction = instruction.instruction;
|
|
||||||
int objectRegisterNumber;
|
int objectRegisterNumber;
|
||||||
|
switch (instruction.getInstruction().getFormat()) {
|
||||||
if (odexedInstruction.getFormat() == Format.Format22cs) {
|
case Format22cs:
|
||||||
objectRegisterNumber = ((Instruction22cs)odexedInstruction).getRegisterB();
|
objectRegisterNumber = ((Instruction22cs)instruction.instruction).getRegisterB();
|
||||||
} else if (odexedInstruction.getFormat() == Format.Format35ms) {
|
break;
|
||||||
objectRegisterNumber = ((Instruction35ms)odexedInstruction).getRegisterD();
|
case Format35ms:
|
||||||
} else if (odexedInstruction.getFormat() == Format.Format3rms) {
|
objectRegisterNumber = ((Instruction35ms)instruction.instruction).getRegisterD();
|
||||||
objectRegisterNumber = ((Instruction3rms)odexedInstruction).getStartRegister();
|
break;
|
||||||
} else {
|
case Format3rms:
|
||||||
assert false;
|
objectRegisterNumber = ((Instruction3rms)instruction.instruction).getStartRegister();
|
||||||
throw new ExceptionWithContext(String.format("Unexpected format %s for odexed instruction",
|
break;
|
||||||
odexedInstruction.getFormat().name()));
|
default:
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
instruction.setDeodexedInstruction(new UnresolvedNullReference(odexedInstruction,
|
instruction.setDeodexedInstruction(new UnresolvedOdexInstruction(instruction.instruction,
|
||||||
objectRegisterNumber));
|
objectRegisterNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,47 +397,6 @@ public class MethodAnalyzer {
|
|||||||
registerType);
|
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,
|
private void setPostRegisterTypeAndPropagateChanges(AnalyzedInstruction analyzedInstruction, int registerNumber,
|
||||||
RegisterType registerType) {
|
RegisterType registerType) {
|
||||||
|
|
||||||
@ -658,6 +620,10 @@ public class MethodAnalyzer {
|
|||||||
return exceptionHandlers;
|
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) {
|
private boolean analyzeInstruction(AnalyzedInstruction analyzedInstruction) {
|
||||||
Instruction instruction = analyzedInstruction.instruction;
|
Instruction instruction = analyzedInstruction.instruction;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public enum Format {
|
|||||||
ArrayData(null, -1, true),
|
ArrayData(null, -1, true),
|
||||||
PackedSwitchData(null, -1, true),
|
PackedSwitchData(null, -1, true),
|
||||||
SparseSwitchData(null, -1, true),
|
SparseSwitchData(null, -1, true),
|
||||||
UnresolvedNullReference(null, -1, false),
|
UnresolvedOdexInstruction(null, -1, false),
|
||||||
;
|
;
|
||||||
|
|
||||||
public final Instruction.InstructionFactory Factory;
|
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
|
* can't be determined. Typically, these are replaced by an equivalent instruction that would have the same
|
||||||
* effect (namely, an NPE)
|
* effect (namely, an NPE)
|
||||||
*/
|
*/
|
||||||
public class UnresolvedNullReference extends Instruction {
|
public class UnresolvedOdexInstruction extends Instruction {
|
||||||
public final Instruction OriginalInstruction;
|
public final Instruction OriginalInstruction;
|
||||||
//the register number that holds the (null) reference type that the instruction operates on
|
//the register number that holds the (null) reference type that the instruction operates on
|
||||||
public final int ObjectRegisterNum;
|
public final int ObjectRegisterNum;
|
||||||
|
|
||||||
public UnresolvedNullReference(Instruction originalInstruction, int objectRegisterNumber) {
|
public UnresolvedOdexInstruction(Instruction originalInstruction, int objectRegisterNumber) {
|
||||||
super(originalInstruction.opcode);
|
super(originalInstruction.opcode);
|
||||||
this.OriginalInstruction = originalInstruction;
|
this.OriginalInstruction = originalInstruction;
|
||||||
this.ObjectRegisterNum = objectRegisterNumber;
|
this.ObjectRegisterNum = objectRegisterNumber;
|
||||||
@ -57,6 +57,6 @@ public class UnresolvedNullReference extends Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Format getFormat() {
|
public Format getFormat() {
|
||||||
return Format.UnresolvedNullReference;
|
return Format.UnresolvedOdexInstruction;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user