mirror of
https://github.com/revanced/smali.git
synced 2025-05-05 00:54:25 +02:00
Preliminary implementation of "verbose register info" functionality
git-svn-id: https://smali.googlecode.com/svn/trunk@620 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
caea37a7d2
commit
7e24a9f010
@ -0,0 +1,25 @@
|
||||
package org.jf.baksmali.Adaptors;
|
||||
|
||||
import org.antlr.stringtemplate.StringTemplate;
|
||||
import org.antlr.stringtemplate.StringTemplateGroup;
|
||||
|
||||
public class CommentMethodItem extends MethodItem {
|
||||
private final StringTemplate template;
|
||||
private final int sortOrder;
|
||||
|
||||
public CommentMethodItem(StringTemplateGroup stg, String comment, int codeAddress, int sortOrder) {
|
||||
super(codeAddress);
|
||||
template = stg.getInstanceOf("Comment");
|
||||
template.setAttribute("Comment", comment);
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public int getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return template.toString();
|
||||
}
|
||||
}
|
@ -31,6 +31,9 @@ package org.jf.baksmali.Adaptors;
|
||||
import org.jf.baksmali.Adaptors.Format.*;
|
||||
import org.jf.baksmali.baksmali;
|
||||
import org.jf.dexlib.*;
|
||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
||||
import org.jf.dexlib.Code.Analysis.RegisterType;
|
||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
||||
import org.jf.dexlib.Code.Instruction;
|
||||
import org.jf.dexlib.Code.Opcode;
|
||||
@ -45,6 +48,7 @@ import java.util.*;
|
||||
public class MethodDefinition {
|
||||
private final StringTemplateGroup stg;
|
||||
private final ClassDataItem.EncodedMethod encodedMethod;
|
||||
private final MethodAnalyzer methodAnalyzer;
|
||||
|
||||
private final LabelCache labelCache = new LabelCache();
|
||||
|
||||
@ -59,7 +63,8 @@ public class MethodDefinition {
|
||||
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
||||
|
||||
if (encodedMethod.codeItem != null) {
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
methodAnalyzer = new MethodAnalyzer(encodedMethod);
|
||||
AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
|
||||
|
||||
packedSwitchMap = new SparseIntArray(1);
|
||||
sparseSwitchMap = new SparseIntArray(1);
|
||||
@ -67,23 +72,24 @@ public class MethodDefinition {
|
||||
|
||||
int currentCodeAddress = 0;
|
||||
for (int i=0; i<instructions.length; i++) {
|
||||
Instruction instruction = instructions[i];
|
||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||
AnalyzedInstruction instruction = instructions[i];
|
||||
if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
|
||||
packedSwitchMap.append(
|
||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||
} else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
|
||||
sparseSwitchMap.append(
|
||||
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
|
||||
currentCodeAddress);
|
||||
}
|
||||
instructionMap.append(currentCodeAddress, i);
|
||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
||||
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
|
||||
}
|
||||
} else {
|
||||
packedSwitchMap = null;
|
||||
sparseSwitchMap = null;
|
||||
instructionMap = null;
|
||||
methodAnalyzer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,20 +233,35 @@ public class MethodDefinition {
|
||||
return methodItems;
|
||||
}
|
||||
|
||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
||||
AnalyzedInstruction[] instructions;
|
||||
if (baksmali.verboseRegisterInfo) {
|
||||
instructions = methodAnalyzer.analyze();
|
||||
} else {
|
||||
instructions = methodAnalyzer.makeInstructionArray();
|
||||
}
|
||||
|
||||
int currentCodeAddress = 0;
|
||||
for (int i=0; i<instructions.length; i++) {
|
||||
Instruction instruction = instructions[i];
|
||||
AnalyzedInstruction instruction = instructions[i];
|
||||
|
||||
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||
encodedMethod.codeItem, currentCodeAddress, stg, instruction));
|
||||
encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
|
||||
|
||||
if (i != instructions.length - 1) {
|
||||
methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
|
||||
}
|
||||
|
||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
||||
if (baksmali.verboseRegisterInfo) {
|
||||
if (instruction.getPredecessorCount() > 1 || i == 0) {
|
||||
methodItems.add(new CommentMethodItem(stg, getPreInstructionRegisterString(instruction),
|
||||
currentCodeAddress, Integer.MIN_VALUE));
|
||||
}
|
||||
methodItems.add(new CommentMethodItem(stg, getPostInstructionRegisterString(instruction),
|
||||
currentCodeAddress, Integer.MAX_VALUE-1));
|
||||
}
|
||||
|
||||
|
||||
currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
|
||||
}
|
||||
|
||||
addTries(methodItems);
|
||||
@ -264,6 +285,41 @@ public class MethodDefinition {
|
||||
return methodItems;
|
||||
}
|
||||
|
||||
private String getPreInstructionRegisterString(AnalyzedInstruction instruction) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i=0; i<instruction.getRegisterCount(); i++) {
|
||||
RegisterType registerType = instruction.getPreInstructionRegisterType(i);
|
||||
sb.append("v");
|
||||
sb.append(i);
|
||||
sb.append("=");
|
||||
if (registerType == null) {
|
||||
sb.append("null");
|
||||
} else {
|
||||
sb.append(registerType.toString());
|
||||
}
|
||||
sb.append(";");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getPostInstructionRegisterString(AnalyzedInstruction instruction) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i=0; i<instruction.getRegisterCount(); i++) {
|
||||
RegisterType registerType = instruction.getPostInstructionRegisterType(i);
|
||||
sb.append("v");
|
||||
sb.append(i);
|
||||
sb.append("=");
|
||||
sb.append(registerType.toString());
|
||||
sb.append(";");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private void addTries(List<MethodItem> methodItems) {
|
||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
|
||||
return;
|
||||
|
@ -33,6 +33,7 @@ import org.antlr.stringtemplate.StringTemplateGroup;
|
||||
import org.jf.baksmali.Adaptors.ClassDefinition;
|
||||
import org.jf.baksmali.Deodex.*;
|
||||
import org.jf.baksmali.Renderers.*;
|
||||
import org.jf.dexlib.Code.Analysis.ClassPath;
|
||||
import org.jf.dexlib.DexFile;
|
||||
import org.jf.dexlib.ClassDefItem;
|
||||
import org.jf.dexlib.StringIdItem;
|
||||
@ -44,16 +45,25 @@ public class baksmali {
|
||||
public static boolean useLocalsDirective = false;
|
||||
public static boolean useSequentialLabels = false;
|
||||
public static boolean outputDebugInfo = true;
|
||||
public static boolean verboseRegisterInfo = false;
|
||||
public static String bootClassPath;
|
||||
public static DeodexUtil deodexUtil = null;
|
||||
|
||||
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
|
||||
boolean noParameterRegisters, boolean useLocalsDirective,
|
||||
boolean useSequentialLabels, boolean outputDebugInfo)
|
||||
String bootClassPath, boolean noParameterRegisters,
|
||||
boolean useLocalsDirective, boolean useSequentialLabels,
|
||||
boolean outputDebugInfo, boolean verboseRegisterInfo)
|
||||
{
|
||||
baksmali.noParameterRegisters = noParameterRegisters;
|
||||
baksmali.useLocalsDirective = useLocalsDirective;
|
||||
baksmali.useSequentialLabels = useSequentialLabels;
|
||||
baksmali.outputDebugInfo = outputDebugInfo;
|
||||
baksmali.verboseRegisterInfo = verboseRegisterInfo;
|
||||
baksmali.bootClassPath = bootClassPath;
|
||||
|
||||
if (verboseRegisterInfo) {
|
||||
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
|
||||
}
|
||||
|
||||
if (deodexerant != null) {
|
||||
baksmali.deodexUtil = new DeodexUtil(deodexerant);
|
||||
|
@ -75,13 +75,14 @@ public class main {
|
||||
boolean useLocalsDirective = false;
|
||||
boolean useSequentialLabels = false;
|
||||
boolean outputDebugInfo = true;
|
||||
|
||||
boolean verboseRegisterInfo = false;
|
||||
|
||||
String outputDirectory = "out";
|
||||
String dumpFileName = null;
|
||||
String outputDexFileName = null;
|
||||
String inputDexFileName = null;
|
||||
String deodexerantHost = null;
|
||||
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
||||
int deodexerantPort = 0;
|
||||
|
||||
String[] remainingArgs = commandLine.getArgs();
|
||||
@ -145,6 +146,14 @@ public class main {
|
||||
outputDebugInfo = false;
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("r")) {
|
||||
verboseRegisterInfo = true;
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("c")) {
|
||||
bootClassPath = commandLine.getOptionValue("c");
|
||||
}
|
||||
|
||||
if (commandLine.hasOption("x")) {
|
||||
String deodexerantAddress = commandLine.getOptionValue("x");
|
||||
String[] parts = deodexerantAddress.split(":");
|
||||
@ -200,8 +209,8 @@ public class main {
|
||||
}
|
||||
|
||||
if (disassemble) {
|
||||
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, noParameterRegisters,
|
||||
useLocalsDirective, useSequentialLabels, outputDebugInfo);
|
||||
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters,
|
||||
useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo);
|
||||
}
|
||||
|
||||
if ((doDump || write) && !dexFile.isOdex()) {
|
||||
@ -310,6 +319,16 @@ public class main {
|
||||
.withDescription("don't write out debug info (.local, .param, .line, etc.)")
|
||||
.create("b");
|
||||
|
||||
Option verboseRegisterInfoOption = OptionBuilder.withLongOpt("verbose-registers")
|
||||
.withDescription("print verbose register type information for each instruction")
|
||||
.create("r");
|
||||
|
||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
||||
.withDescription("the bootclasspath jars to use, for analysis")
|
||||
.hasArg()
|
||||
.withArgName("BOOTCLASSPATH")
|
||||
.create("c");
|
||||
|
||||
options.addOption(versionOption);
|
||||
options.addOption(helpOption);
|
||||
options.addOption(dumpOption);
|
||||
@ -323,5 +342,7 @@ public class main {
|
||||
options.addOption(useLocalsOption);
|
||||
options.addOption(sequentialLabelsOption);
|
||||
options.addOption(noDebugInfoOption);
|
||||
options.addOption(verboseRegisterInfoOption);
|
||||
options.addOption(classPathOption);
|
||||
}
|
||||
}
|
@ -435,3 +435,8 @@ AnnotationElement(Name, Value) ::=
|
||||
<<
|
||||
<Name> = <Value>
|
||||
>>
|
||||
|
||||
Comment(Comment) ::=
|
||||
<<
|
||||
#<Comment>
|
||||
>>
|
@ -12,7 +12,7 @@ public class AnalyzedInstruction {
|
||||
/**
|
||||
* The actual instruction
|
||||
*/
|
||||
protected final Instruction instruction;
|
||||
public final Instruction instruction;
|
||||
|
||||
/**
|
||||
* The index of the instruction, where the first instruction in the method is at index 0, and so on
|
||||
@ -49,12 +49,20 @@ public class AnalyzedInstruction {
|
||||
this.instruction = instruction;
|
||||
this.instructionIndex = instructionIndex;
|
||||
this.postRegisterMap = new RegisterType[registerCount];
|
||||
RegisterType unknown = RegisterType.getRegisterType(RegisterType.Category.Unknown, null);
|
||||
for (int i=0; i<registerCount; i++) {
|
||||
postRegisterMap[i] = unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public int getInstructionIndex() {
|
||||
return instructionIndex;
|
||||
}
|
||||
|
||||
public int getPredecessorCount() {
|
||||
return predecessors.size();
|
||||
}
|
||||
|
||||
protected void addPredecessor(AnalyzedInstruction predecessor) {
|
||||
predecessors.add(predecessor);
|
||||
}
|
||||
@ -104,7 +112,8 @@ public class AnalyzedInstruction {
|
||||
}
|
||||
|
||||
protected boolean isInvokeInit() {
|
||||
if (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE) {
|
||||
if (instruction == null ||
|
||||
(instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -115,7 +124,7 @@ public class AnalyzedInstruction {
|
||||
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
|
||||
MethodIdItem method = (MethodIdItem)item;
|
||||
|
||||
if (!method.getMethodName().equals("<init>")) {
|
||||
if (!method.getMethodName().getStringValue().equals("<init>")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -175,6 +184,14 @@ public class AnalyzedInstruction {
|
||||
return ((SingleRegisterInstruction)instruction).getRegisterA();
|
||||
}
|
||||
|
||||
public int getRegisterCount() {
|
||||
return postRegisterMap.length;
|
||||
}
|
||||
|
||||
public RegisterType getPostInstructionRegisterType(int registerNumber) {
|
||||
return postRegisterMap[registerNumber];
|
||||
}
|
||||
|
||||
public RegisterType getPreInstructionRegisterType(int registerNumber) {
|
||||
//if the specific register is not a destination register, then the stored post-instruction register type will
|
||||
//be the same as the pre-instruction regsiter type, so we can use that.
|
||||
|
@ -15,35 +15,32 @@ public class ClassPath {
|
||||
private static ClassPath theClassPath = null;
|
||||
|
||||
private final HashMap<String, ClassDef> classDefs;
|
||||
protected final ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
|
||||
protected ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
|
||||
|
||||
public static void InitializeClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||
if (theClassPath != null) {
|
||||
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
|
||||
}
|
||||
|
||||
theClassPath = new ClassPath(bootClassPath, dexFile);
|
||||
theClassPath = new ClassPath();
|
||||
theClassPath.initClassPath(bootClassPath, dexFile);
|
||||
}
|
||||
|
||||
private ClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||
private ClassPath() {
|
||||
classDefs = new HashMap<String, ClassDef>();
|
||||
}
|
||||
|
||||
private void initClassPath(String[] bootClassPath, DexFile dexFile) {
|
||||
if (bootClassPath == null || bootClassPath.length == 0) {
|
||||
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
|
||||
}
|
||||
|
||||
classDefs = new HashMap<String, ClassDef>();
|
||||
|
||||
for (String bootClassPathEntry: bootClassPath) {
|
||||
loadBootClassPath(bootClassPathEntry);
|
||||
}
|
||||
|
||||
loadDexFile(dexFile);
|
||||
|
||||
try {
|
||||
javaLangObjectClassDef = getClassDef("Ljava/lang/Object;");
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw ExceptionWithContext.withContext(ex, "Ljava/lang/Object; must be present in the classpath");
|
||||
}
|
||||
|
||||
for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
|
||||
ClassDef classDef = new PrimitiveClassDef(primitiveType);
|
||||
classDefs.put(primitiveType, classDef);
|
||||
@ -77,8 +74,12 @@ public class ClassPath {
|
||||
//TODO: need to check if the class already exists. (and if so, what to do about it?)
|
||||
ClassDef classDef = new ClassDef(classDefItem);
|
||||
classDefs.put(classDef.getClassType(), classDef);
|
||||
classDef.dumpVtable();
|
||||
classDef.dumpFields();
|
||||
|
||||
if (classDefItem.getClassType().getTypeDescriptor().equals("Ljava/lang/Object;")) {
|
||||
theClassPath.javaLangObjectClassDef = classDef;
|
||||
}
|
||||
/*classDef.dumpVtable();
|
||||
classDef.dumpFields();*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +275,7 @@ public class ClassPath {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClassDef {
|
||||
public static class ClassDef implements Comparable<ClassDef> {
|
||||
private final String classType;
|
||||
private final ClassDef superclass;
|
||||
/**
|
||||
@ -329,7 +330,7 @@ public class ClassPath {
|
||||
}
|
||||
}
|
||||
|
||||
protected ClassDef(ClassDefItem classDefItem) {
|
||||
protected ClassDef(ClassDefItem classDefItem) {
|
||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||
|
||||
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
|
||||
@ -406,23 +407,23 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
//TODO: GROT
|
||||
public void dumpVtable() {
|
||||
/*public void dumpVtable() {
|
||||
System.out.println(classType + " methods:");
|
||||
int i=0;
|
||||
for (String method: vtable) {
|
||||
System.out.println(i + ":\t" + method);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
//TODO: GROT
|
||||
public void dumpFields() {
|
||||
/*public void dumpFields() {
|
||||
System.out.println(classType + " fields:");
|
||||
for (int i=0; i<instanceFields.size(); i++) {
|
||||
int fieldOffset = instanceFields.keyAt(i);
|
||||
System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
|
||||
byte tempType = fieldTypes[position1];
|
||||
@ -725,5 +726,9 @@ public class ClassPath {
|
||||
public int hashCode() {
|
||||
return classType.hashCode();
|
||||
}
|
||||
|
||||
public int compareTo(ClassDef classDef) {
|
||||
return classType.compareTo(classDef.classType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ public class MethodAnalyzer {
|
||||
throw new IllegalArgumentException("The method has no code");
|
||||
}
|
||||
this.encodedMethod = encodedMethod;
|
||||
buildInstructionList();
|
||||
|
||||
//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
|
||||
@ -57,6 +56,8 @@ public class MethodAnalyzer {
|
||||
};
|
||||
};
|
||||
|
||||
buildInstructionList();
|
||||
|
||||
instructionsToVerify = new BitSet(instructions.size());
|
||||
}
|
||||
|
||||
@ -82,7 +83,7 @@ public class MethodAnalyzer {
|
||||
//if this is a constructor, then set the "this" register to an uninitialized reference of the current class
|
||||
if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) {
|
||||
//TODO: it would probably make more sense to validate this somewhere else, and just put an assert here. Also, need to do a similar check for static constructor
|
||||
if (!encodedMethod.method.getMethodName().equals("<init>")) {
|
||||
if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||
throw new ValidationException("The constructor flag can only be used with an <init> method.");
|
||||
}
|
||||
|
||||
@ -90,7 +91,7 @@ public class MethodAnalyzer {
|
||||
RegisterType.getRegisterType(RegisterType.Category.UninitRef,
|
||||
ClassPath.getClassDef(methodIdItem.getContainingClass())));
|
||||
} else {
|
||||
if (encodedMethod.method.getMethodName().equals("<init>")) {
|
||||
if (encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||
throw new ValidationException("An <init> method must have the \"constructor\" access flag");
|
||||
}
|
||||
|
||||
@ -119,8 +120,8 @@ public class MethodAnalyzer {
|
||||
|
||||
while (!instructionsToVerify.isEmpty()) {
|
||||
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
|
||||
analyzeInstruction(instructions.get(i));
|
||||
instructionsToVerify.clear(i);
|
||||
analyzeInstruction(instructions.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +202,7 @@ public class MethodAnalyzer {
|
||||
|
||||
boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType);
|
||||
|
||||
if (!changed || analyzedInstruction.setsRegister(registerNumber)) {
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -216,7 +217,7 @@ public class MethodAnalyzer {
|
||||
while (!changedInstructions.isEmpty()) {
|
||||
for (int instructionIndex=changedInstructions.nextSetBit(0);
|
||||
instructionIndex>=0;
|
||||
instructionIndex=changedInstructions.nextSetBit(instructionIndex)) {
|
||||
instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
|
||||
|
||||
changedInstructions.clear(instructionIndex);
|
||||
|
||||
@ -242,9 +243,10 @@ public class MethodAnalyzer {
|
||||
if (!successor.setsRegister(registerNumber)) {
|
||||
RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(registerNumber);
|
||||
|
||||
if (registerType.category == RegisterType.Category.UninitRef && instruction.isInvokeInit()) {
|
||||
//TODO: GROT?
|
||||
/*if (registerType.category == RegisterType.Category.UninitRef && instruction.isInvokeInit()) {
|
||||
continue;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (successor.setPostRegisterType(registerNumber, registerType)) {
|
||||
changedInstructions.set(successor.instructionIndex);
|
||||
@ -261,8 +263,6 @@ public class MethodAnalyzer {
|
||||
assert encodedMethod.codeItem != null;
|
||||
int registerCount = encodedMethod.codeItem.getRegisterCount();
|
||||
|
||||
startOfMethod = new AnalyzedInstruction(null, -1, registerCount);
|
||||
|
||||
Instruction[] insns = encodedMethod.codeItem.getInstructions();
|
||||
|
||||
instructions = new SparseArray<AnalyzedInstruction>(insns.length);
|
||||
@ -284,34 +284,36 @@ public class MethodAnalyzer {
|
||||
AnalyzedInstruction[] currentExceptionHandlers = null;
|
||||
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
|
||||
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||
if (tries != null) {
|
||||
for (int i=0; i<instructions.size(); i++) {
|
||||
AnalyzedInstruction instruction = instructions.valueAt(i);
|
||||
Opcode instructionOpcode = instruction.instruction.opcode;
|
||||
|
||||
//check if we have gone past the end of the current try
|
||||
if (currentTry != null) {
|
||||
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
||||
currentTry = null;
|
||||
triesIndex++;
|
||||
//check if we have gone past the end of the current try
|
||||
if (currentTry != null) {
|
||||
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
|
||||
currentTry = null;
|
||||
triesIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if the next try is applicable yet
|
||||
if (currentTry == null && triesIndex < tries.length) {
|
||||
CodeItem.TryItem tryItem = tries[triesIndex];
|
||||
if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
|
||||
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
|
||||
//check if the next try is applicable yet
|
||||
if (currentTry == null && triesIndex < tries.length) {
|
||||
CodeItem.TryItem tryItem = tries[triesIndex];
|
||||
if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
|
||||
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > currentCodeAddress);
|
||||
|
||||
currentTry = tryItem;
|
||||
currentTry = tryItem;
|
||||
|
||||
currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
|
||||
currentExceptionHandlers = buildExceptionHandlerArray(tryItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
|
||||
//for the current instruction
|
||||
if (currentTry != null && instructionOpcode.canThrow()) {
|
||||
exceptionHandlers[i] = currentExceptionHandlers;
|
||||
//if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
|
||||
//for the current instruction
|
||||
if (currentTry != null && instructionOpcode.canThrow()) {
|
||||
exceptionHandlers[i] = currentExceptionHandlers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,8 +335,8 @@ public class MethodAnalyzer {
|
||||
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
|
||||
}
|
||||
|
||||
if (instruction instanceof OffsetInstruction) {
|
||||
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
|
||||
if (instruction.instruction instanceof OffsetInstruction) {
|
||||
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
|
||||
|
||||
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
|
||||
MultiOffsetInstruction switchDataInstruction =
|
||||
@ -431,7 +433,7 @@ public class MethodAnalyzer {
|
||||
case MOVE_OBJECT:
|
||||
case MOVE_OBJECT_FROM16:
|
||||
case MOVE_OBJECT_16:
|
||||
handleMove(analyzedInstruction, ReferenceCategories);
|
||||
handleMove(analyzedInstruction, ReferenceOrUninitCategories);
|
||||
return;
|
||||
case MOVE_RESULT:
|
||||
handleMoveResult(analyzedInstruction, Primitive32BitCategories);
|
||||
@ -904,9 +906,12 @@ public class MethodAnalyzer {
|
||||
|
||||
private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
|
||||
RegisterType.Category.Null,
|
||||
RegisterType.Category.One,
|
||||
RegisterType.Category.Boolean,
|
||||
RegisterType.Category.Byte,
|
||||
RegisterType.Category.PosByte,
|
||||
RegisterType.Category.Short,
|
||||
RegisterType.Category.PosShort,
|
||||
RegisterType.Category.Char,
|
||||
RegisterType.Category.Integer,
|
||||
RegisterType.Category.Float);
|
||||
@ -923,11 +928,19 @@ public class MethodAnalyzer {
|
||||
RegisterType.Category.Null,
|
||||
RegisterType.Category.Reference);
|
||||
|
||||
private static final EnumSet<RegisterType.Category> ReferenceOrUninitCategories = EnumSet.of(
|
||||
RegisterType.Category.Null,
|
||||
RegisterType.Category.UninitRef,
|
||||
RegisterType.Category.Reference);
|
||||
|
||||
private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
|
||||
RegisterType.Category.Null,
|
||||
RegisterType.Category.One,
|
||||
RegisterType.Category.Boolean,
|
||||
RegisterType.Category.Byte,
|
||||
RegisterType.Category.PosByte,
|
||||
RegisterType.Category.Short,
|
||||
RegisterType.Category.PosShort,
|
||||
RegisterType.Category.Char,
|
||||
RegisterType.Category.Integer,
|
||||
RegisterType.Category.Float,
|
||||
@ -1037,8 +1050,9 @@ public class MethodAnalyzer {
|
||||
if (thisRegisterType.category == RegisterType.Category.UninitRef) {
|
||||
throw new ValidationException("Returning from constructor without calling the superclass' <init>");
|
||||
}
|
||||
assert thisRegisterType.category == RegisterType.Category.Reference;
|
||||
assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||
//TODO: GROT
|
||||
//assert thisRegisterType.category == RegisterType.Category.Reference;
|
||||
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
|
||||
}
|
||||
|
||||
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
||||
@ -2285,8 +2299,8 @@ public class MethodAnalyzer {
|
||||
}
|
||||
|
||||
if (isInit) {
|
||||
if (objectRegisterType.type == methodClassDef.getSuperclass()) {
|
||||
if (!encodedMethod.method.getMethodName().equals("<init>")) {
|
||||
if (objectRegisterType.type.getSuperclass() == methodClassDef) {
|
||||
if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
|
||||
throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
|
||||
"match the method type exactly", methodIdItem.getMethodString(),
|
||||
objectRegisterType.type.getClassType()));
|
||||
@ -2302,44 +2316,46 @@ public class MethodAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
||||
int parameterTypeIndex = 0;
|
||||
while (!registers.pastEnd()) {
|
||||
assert parameterTypeIndex < parameterTypes.size();
|
||||
RegisterType parameterType =
|
||||
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
||||
if (typeListItem != null) {
|
||||
List<TypeIdItem> parameterTypes = typeListItem.getTypes();
|
||||
int parameterTypeIndex = 0;
|
||||
while (!registers.pastEnd()) {
|
||||
assert parameterTypeIndex < parameterTypes.size();
|
||||
RegisterType parameterType =
|
||||
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
|
||||
|
||||
int register = registers.getRegister();
|
||||
int register = registers.getRegister();
|
||||
|
||||
RegisterType parameterRegisterType;
|
||||
if (WideLowCategories.contains(parameterType.category)) {
|
||||
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
||||
RegisterType parameterRegisterType;
|
||||
if (WideLowCategories.contains(parameterType.category)) {
|
||||
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
|
||||
|
||||
if (!registers.moveNext()) {
|
||||
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
||||
parameterTypeIndex+1));
|
||||
if (!registers.moveNext()) {
|
||||
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
|
||||
parameterTypeIndex+1));
|
||||
}
|
||||
int nextRegister = registers.getRegister();
|
||||
|
||||
if (nextRegister != register + 1) {
|
||||
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
|
||||
"must be consecutive.", register, nextRegister));
|
||||
}
|
||||
} else {
|
||||
parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
|
||||
}
|
||||
int nextRegister = registers.getRegister();
|
||||
|
||||
if (nextRegister != register + 1) {
|
||||
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
|
||||
"must be consecutive.", register, nextRegister));
|
||||
assert parameterRegisterType != null;
|
||||
|
||||
if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
|
||||
throw new ValidationException(
|
||||
String.format("Invalid register type %s for parameter %d %s.",
|
||||
parameterRegisterType.toString(), parameterTypeIndex+1,
|
||||
parameterType.toString()));
|
||||
}
|
||||
} else {
|
||||
parameterRegisterType = analyzedInstruction.getPreInstructionRegisterType(register);
|
||||
|
||||
parameterTypeIndex++;
|
||||
registers.moveNext();
|
||||
}
|
||||
|
||||
assert parameterRegisterType != null;
|
||||
|
||||
if (!parameterRegisterType.canBeAssignedTo(parameterType)) {
|
||||
throw new ValidationException(
|
||||
String.format("Invalid register type %s for parameter %d %s.",
|
||||
parameterRegisterType.toString(), parameterTypeIndex+1,
|
||||
parameterType.toString()));
|
||||
}
|
||||
|
||||
parameterTypeIndex++;
|
||||
registers.moveNext();
|
||||
}
|
||||
|
||||
|
||||
|
@ -222,18 +222,18 @@ public class RegisterType {
|
||||
}
|
||||
|
||||
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()];
|
||||
if (mergedCategory == Category.Conflicted) {
|
||||
throw new ValidationException("Incompatible register types." +
|
||||
" Category1: " + this.category.name() +
|
||||
(this.type==null?"":" Type1: " + this.type.getClassType()) +
|
||||
" Category2: " + type.category.name() +
|
||||
(type.type==null?"":" Type2: " + type.type.getClassType()));
|
||||
}
|
||||
|
||||
ClassDef mergedType = null;
|
||||
if (mergedCategory == Category.Reference) {
|
||||
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
||||
}
|
||||
if (mergedCategory == Category.UninitRef) {
|
||||
if (this.category == Category.Unknown) {
|
||||
return type;
|
||||
}
|
||||
assert type.category == Category.Unknown;
|
||||
return this;
|
||||
}
|
||||
return RegisterType.getRegisterType(mergedCategory, mergedType);
|
||||
}
|
||||
|
||||
|
@ -281,9 +281,9 @@ public enum Opcode
|
||||
//if the instruction sets the "hidden" result register
|
||||
public static final int SETS_RESULT = 0x8;
|
||||
//if the instruction sets the value of it's first register
|
||||
public static final int SETS_REGISTER = 0x16;
|
||||
public static final int SETS_REGISTER = 0x10;
|
||||
//if the instruction sets the value of it's first register to a wide type
|
||||
public static final int SETS_WIDE_REGISTER = 0x32;
|
||||
public static final int SETS_WIDE_REGISTER = 0x20;
|
||||
|
||||
static {
|
||||
opcodesByValue = new Opcode[256];
|
||||
|
Loading…
x
Reference in New Issue
Block a user