mirror of
https://github.com/revanced/smali.git
synced 2025-05-30 20:40:11 +02:00
Improve how the instance-of + if-eqz/if-nez type propagation works
We now perform the type propagation while analyzing the if-eqz/if-nez instruction. Additionally, AnalyzedInstruction.setsRegister now has special case logic to check for this case, so we don't incorrectly propagate the original type past the if-eqz/if-nez
This commit is contained in:
parent
9802cf3428
commit
93100e57b2
@ -33,7 +33,9 @@ package org.jf.dexlib2.analysis;
|
|||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
import org.jf.dexlib2.Opcode;
|
||||||
import org.jf.dexlib2.iface.instruction.*;
|
import org.jf.dexlib2.iface.instruction.*;
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.Instruction22c;
|
||||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||||
import org.jf.dexlib2.iface.reference.Reference;
|
import org.jf.dexlib2.iface.reference.Reference;
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
@ -43,9 +45,16 @@ import javax.annotation.Nullable;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
||||||
|
/**
|
||||||
|
* The MethodAnalyzer containing this instruction
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
protected final MethodAnalyzer methodAnalyzer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual instruction
|
* The actual instruction
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
protected Instruction instruction;
|
protected Instruction instruction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,7 +94,9 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
|||||||
*/
|
*/
|
||||||
protected final Instruction originalInstruction;
|
protected final Instruction originalInstruction;
|
||||||
|
|
||||||
public AnalyzedInstruction(Instruction instruction, int instructionIndex, int registerCount) {
|
public AnalyzedInstruction(MethodAnalyzer methodAnalyzer, Instruction instruction, int instructionIndex,
|
||||||
|
int registerCount) {
|
||||||
|
this.methodAnalyzer = methodAnalyzer;
|
||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
this.originalInstruction = instruction;
|
this.originalInstruction = instruction;
|
||||||
this.instructionIndex = instructionIndex;
|
this.instructionIndex = instructionIndex;
|
||||||
@ -353,6 +364,17 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (instruction.getOpcode() == Opcode.IF_EQZ || instruction.getOpcode() == Opcode.IF_NEZ) {
|
||||||
|
AnalyzedInstruction previousInstruction = getPreviousInstruction();
|
||||||
|
if (previousInstruction != null &&
|
||||||
|
previousInstruction.instruction != null &&
|
||||||
|
previousInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF &&
|
||||||
|
registerNumber == ((Instruction22c)previousInstruction.instruction).getRegisterB() &&
|
||||||
|
MethodAnalyzer.canNarrowAfterInstanceOf(previousInstruction, this, methodAnalyzer.getClassPath())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!setsRegister()) {
|
if (!setsRegister()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -367,6 +389,16 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private AnalyzedInstruction getPreviousInstruction() {
|
||||||
|
for (AnalyzedInstruction predecessor: predecessors) {
|
||||||
|
if (predecessor.getInstructionIndex() == getInstructionIndex() - 1) {
|
||||||
|
return predecessor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public int getDestinationRegister() {
|
public int getDestinationRegister() {
|
||||||
if (!this.instruction.getOpcode().setsRegister()) {
|
if (!this.instruction.getOpcode().setsRegister()) {
|
||||||
throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
|
throw new ExceptionWithContext("Cannot call getDestinationRegister() for an instruction that doesn't " +
|
||||||
|
@ -112,7 +112,7 @@ public class MethodAnalyzer {
|
|||||||
|
|
||||||
//override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
|
//override AnalyzedInstruction and provide custom implementations of some of the methods, so that we don't
|
||||||
//have to handle the case this special case of instruction being null, in the main class
|
//have to handle the case this special case of instruction being null, in the main class
|
||||||
startOfMethod = new AnalyzedInstruction(null, -1, methodImpl.getRegisterCount()) {
|
startOfMethod = new AnalyzedInstruction(this, null, -1, methodImpl.getRegisterCount()) {
|
||||||
public boolean setsRegister() {
|
public boolean setsRegister() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -141,6 +141,10 @@ public class MethodAnalyzer {
|
|||||||
analyze();
|
analyze();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassPath getClassPath() {
|
||||||
|
return classPath;
|
||||||
|
}
|
||||||
|
|
||||||
private void analyze() {
|
private void analyze() {
|
||||||
Method method = this.method;
|
Method method = this.method;
|
||||||
MethodImplementation methodImpl = this.methodImpl;
|
MethodImplementation methodImpl = this.methodImpl;
|
||||||
@ -422,7 +426,8 @@ public class MethodAnalyzer {
|
|||||||
int currentCodeAddress = 0;
|
int currentCodeAddress = 0;
|
||||||
for (int i=0; i<instructions.size(); i++) {
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
Instruction instruction = instructions.get(i);
|
Instruction instruction = instructions.get(i);
|
||||||
analyzedInstructions.append(currentCodeAddress, new AnalyzedInstruction(instruction, i, registerCount));
|
analyzedInstructions.append(currentCodeAddress,
|
||||||
|
new AnalyzedInstruction(this, instruction, i, registerCount));
|
||||||
assert analyzedInstructions.indexOfKey(currentCodeAddress) == i;
|
assert analyzedInstructions.indexOfKey(currentCodeAddress) == i;
|
||||||
currentCodeAddress += instruction.getCodeUnits();
|
currentCodeAddress += instruction.getCodeUnits();
|
||||||
}
|
}
|
||||||
@ -683,13 +688,15 @@ public class MethodAnalyzer {
|
|||||||
case IF_GE:
|
case IF_GE:
|
||||||
case IF_GT:
|
case IF_GT:
|
||||||
case IF_LE:
|
case IF_LE:
|
||||||
case IF_EQZ:
|
|
||||||
case IF_NEZ:
|
|
||||||
case IF_LTZ:
|
case IF_LTZ:
|
||||||
case IF_GEZ:
|
case IF_GEZ:
|
||||||
case IF_GTZ:
|
case IF_GTZ:
|
||||||
case IF_LEZ:
|
case IF_LEZ:
|
||||||
return true;
|
return true;
|
||||||
|
case IF_EQZ:
|
||||||
|
case IF_NEZ:
|
||||||
|
analyzeIfEqzNez(analyzedInstruction);
|
||||||
|
return true;
|
||||||
case AGET:
|
case AGET:
|
||||||
analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.INTEGER_TYPE);
|
analyze32BitPrimitiveAget(analyzedInstruction, RegisterType.INTEGER_TYPE);
|
||||||
return true;
|
return true;
|
||||||
@ -1169,53 +1176,81 @@ public class MethodAnalyzer {
|
|||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
|
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, castRegisterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
static boolean canNarrowAfterInstanceOf(AnalyzedInstruction analyzedInstanceOfInstruction,
|
||||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
|
AnalyzedInstruction analyzedIfInstruction, ClassPath classPath) {
|
||||||
|
Instruction ifInstruction = analyzedIfInstruction.instruction;
|
||||||
|
assert analyzedIfInstruction.instruction != null;
|
||||||
|
if (((Instruction21t)ifInstruction).getRegisterA() == analyzedInstanceOfInstruction.getDestinationRegister()) {
|
||||||
|
Reference reference = ((Instruction22c)analyzedInstanceOfInstruction.getInstruction()).getReference();
|
||||||
|
RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
|
||||||
|
|
||||||
int instructionIndex = analyzedInstruction.getInstructionIndex();
|
if (registerType.type != null && !registerType.type.isInterface()) {
|
||||||
AnalyzedInstruction nextAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex + 1);
|
int objectRegister = ((TwoRegisterInstruction)analyzedInstanceOfInstruction.getInstruction())
|
||||||
|
.getRegisterB();
|
||||||
|
|
||||||
Instruction nextInstruction = nextAnalyzedInstruction.instruction;
|
RegisterType originalType = analyzedIfInstruction.getPreInstructionRegisterType(objectRegister);
|
||||||
if (nextInstruction.getOpcode() == Opcode.IF_EQZ || nextInstruction.getOpcode() == Opcode.IF_NEZ) {
|
|
||||||
if (((Instruction21t)nextInstruction).getRegisterA() == analyzedInstruction.getDestinationRegister()) {
|
|
||||||
Reference reference = ((Instruction22c)analyzedInstruction.getInstruction()).getReference();
|
|
||||||
RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
|
|
||||||
|
|
||||||
if (registerType.type != null && !registerType.type.isInterface()) {
|
if (originalType.type != null) {
|
||||||
int objectRegister = ((TwoRegisterInstruction)analyzedInstruction.getInstruction()).getRegisterB();
|
// Only override if we're going from an interface to a class, or are going to a narrower class
|
||||||
|
if (originalType.type.isInterface()) {
|
||||||
RegisterType existingType = nextAnalyzedInstruction.getPostInstructionRegisterType(objectRegister);
|
return true;
|
||||||
|
} else {
|
||||||
if (existingType.type != null) {
|
TypeProto commonSuperclass = registerType.type.getCommonSuperclass(originalType.type);
|
||||||
boolean override = false;
|
// only if it's a narrowing conversion
|
||||||
|
if (commonSuperclass.getType().equals(originalType.type.getType())) {
|
||||||
// Only override if we're going from an interface to a class, or are going to a narrower class
|
return true;
|
||||||
if (existingType.type.isInterface()) {
|
|
||||||
override = true;
|
|
||||||
} else {
|
|
||||||
TypeProto commonSuperclass = registerType.type.getCommonSuperclass(existingType.type);
|
|
||||||
// only if it's a narrowing conversion
|
|
||||||
if (commonSuperclass.getType().equals(existingType.type.getType())) {
|
|
||||||
override = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (override) {
|
|
||||||
AnalyzedInstruction branchStartInstruction;
|
|
||||||
if (nextInstruction.getOpcode() == Opcode.IF_EQZ) {
|
|
||||||
branchStartInstruction = analyzedInstructions.valueAt(instructionIndex + 2);
|
|
||||||
} else {
|
|
||||||
int nextAddress = getInstructionAddress(nextAnalyzedInstruction) +
|
|
||||||
((Instruction21t)nextInstruction).getCodeOffset();
|
|
||||||
branchStartInstruction = analyzedInstructions.get(nextAddress);
|
|
||||||
}
|
|
||||||
overridePredecessorRegisterTypeAndPropagateChanges(branchStartInstruction,
|
|
||||||
nextAnalyzedInstruction, objectRegister, registerType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Art uses a peephole optimization for an if-eqz or if-nez that occur immediately after an instance-of. It will
|
||||||
|
* narrow the type if possible, and then NOP out any corresponding check-cast instruction later on
|
||||||
|
*
|
||||||
|
* TODO: Is this still safe to do even for dalvik odexes? I think it should be..
|
||||||
|
*/
|
||||||
|
private void analyzeIfEqzNez(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
||||||
|
int instructionIndex = analyzedInstruction.getInstructionIndex();
|
||||||
|
if (instructionIndex > 0) {
|
||||||
|
AnalyzedInstruction prevAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex - 1);
|
||||||
|
if (prevAnalyzedInstruction.instruction != null &&
|
||||||
|
prevAnalyzedInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF) {
|
||||||
|
if (canNarrowAfterInstanceOf(prevAnalyzedInstruction, analyzedInstruction, classPath)) {
|
||||||
|
// Propagate the original type to the failing branch, and the new type to the successful branch
|
||||||
|
int narrowingRegister = ((Instruction22c)prevAnalyzedInstruction.instruction).getRegisterB();
|
||||||
|
RegisterType originalType = analyzedInstruction.getPreInstructionRegisterType(narrowingRegister);
|
||||||
|
RegisterType newType = RegisterType.getRegisterType(classPath,
|
||||||
|
(TypeReference)((Instruction22c)prevAnalyzedInstruction.instruction).getReference());
|
||||||
|
|
||||||
|
AnalyzedInstruction fallthroughInstruction = analyzedInstructions.valueAt(
|
||||||
|
analyzedInstruction.getInstructionIndex() + 1);
|
||||||
|
|
||||||
|
int nextAddress = getInstructionAddress(analyzedInstruction) +
|
||||||
|
((Instruction21t)analyzedInstruction.instruction).getCodeOffset();
|
||||||
|
AnalyzedInstruction branchInstruction = analyzedInstructions.get(nextAddress);
|
||||||
|
|
||||||
|
if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) {
|
||||||
|
overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction,
|
||||||
|
narrowingRegister, newType);
|
||||||
|
overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
|
||||||
|
narrowingRegister, originalType);
|
||||||
|
} else {
|
||||||
|
overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction,
|
||||||
|
narrowingRegister, originalType);
|
||||||
|
overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction,
|
||||||
|
narrowingRegister, newType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
||||||
|
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user