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:
JesusFreke@JesusFreke.com 2010-02-08 02:28:37 +00:00
parent caea37a7d2
commit 7e24a9f010
10 changed files with 269 additions and 114 deletions

View File

@ -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();
}
}

View File

@ -31,6 +31,9 @@ package org.jf.baksmali.Adaptors;
import org.jf.baksmali.Adaptors.Format.*; import org.jf.baksmali.Adaptors.Format.*;
import org.jf.baksmali.baksmali; import org.jf.baksmali.baksmali;
import org.jf.dexlib.*; 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.Debug.DebugInstructionIterator;
import org.jf.dexlib.Code.Instruction; import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.Opcode; import org.jf.dexlib.Code.Opcode;
@ -45,6 +48,7 @@ import java.util.*;
public class MethodDefinition { public class MethodDefinition {
private final StringTemplateGroup stg; private final StringTemplateGroup stg;
private final ClassDataItem.EncodedMethod encodedMethod; private final ClassDataItem.EncodedMethod encodedMethod;
private final MethodAnalyzer methodAnalyzer;
private final LabelCache labelCache = new LabelCache(); 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. //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
if (encodedMethod.codeItem != null) { if (encodedMethod.codeItem != null) {
Instruction[] instructions = encodedMethod.codeItem.getInstructions(); methodAnalyzer = new MethodAnalyzer(encodedMethod);
AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
packedSwitchMap = new SparseIntArray(1); packedSwitchMap = new SparseIntArray(1);
sparseSwitchMap = new SparseIntArray(1); sparseSwitchMap = new SparseIntArray(1);
@ -67,23 +72,24 @@ public class MethodDefinition {
int currentCodeAddress = 0; int currentCodeAddress = 0;
for (int i=0; i<instructions.length; i++) { for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i]; AnalyzedInstruction instruction = instructions[i];
if (instruction.opcode == Opcode.PACKED_SWITCH) { if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
packedSwitchMap.append( packedSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(), currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
currentCodeAddress); currentCodeAddress);
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) { } else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
sparseSwitchMap.append( sparseSwitchMap.append(
currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(), currentCodeAddress + ((OffsetInstruction)instruction).getTargetAddressOffset(),
currentCodeAddress); currentCodeAddress);
} }
instructionMap.append(currentCodeAddress, i); instructionMap.append(currentCodeAddress, i);
currentCodeAddress += instruction.getSize(currentCodeAddress); currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
} }
} else { } else {
packedSwitchMap = null; packedSwitchMap = null;
sparseSwitchMap = null; sparseSwitchMap = null;
instructionMap = null; instructionMap = null;
methodAnalyzer = null;
} }
} }
@ -227,20 +233,35 @@ public class MethodDefinition {
return methodItems; return methodItems;
} }
Instruction[] instructions = encodedMethod.codeItem.getInstructions(); AnalyzedInstruction[] instructions;
if (baksmali.verboseRegisterInfo) {
instructions = methodAnalyzer.analyze();
} else {
instructions = methodAnalyzer.makeInstructionArray();
}
int currentCodeAddress = 0; int currentCodeAddress = 0;
for (int i=0; i<instructions.length; i++) { for (int i=0; i<instructions.length; i++) {
Instruction instruction = instructions[i]; AnalyzedInstruction instruction = instructions[i];
methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this, methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
encodedMethod.codeItem, currentCodeAddress, stg, instruction)); encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
if (i != instructions.length - 1) { if (i != instructions.length - 1) {
methodItems.add(new BlankMethodItem(stg, currentCodeAddress)); 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); addTries(methodItems);
@ -264,6 +285,41 @@ public class MethodDefinition {
return methodItems; 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) { private void addTries(List<MethodItem> methodItems) {
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) { if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
return; return;

View File

@ -33,6 +33,7 @@ import org.antlr.stringtemplate.StringTemplateGroup;
import org.jf.baksmali.Adaptors.ClassDefinition; import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Deodex.*; import org.jf.baksmali.Deodex.*;
import org.jf.baksmali.Renderers.*; import org.jf.baksmali.Renderers.*;
import org.jf.dexlib.Code.Analysis.ClassPath;
import org.jf.dexlib.DexFile; import org.jf.dexlib.DexFile;
import org.jf.dexlib.ClassDefItem; import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.StringIdItem; import org.jf.dexlib.StringIdItem;
@ -44,16 +45,25 @@ public class baksmali {
public static boolean useLocalsDirective = false; public static boolean useLocalsDirective = false;
public static boolean useSequentialLabels = false; public static boolean useSequentialLabels = false;
public static boolean outputDebugInfo = true; public static boolean outputDebugInfo = true;
public static boolean verboseRegisterInfo = false;
public static String bootClassPath;
public static DeodexUtil deodexUtil = null; public static DeodexUtil deodexUtil = null;
public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory, public static void disassembleDexFile(DexFile dexFile, Deodexerant deodexerant, String outputDirectory,
boolean noParameterRegisters, boolean useLocalsDirective, String bootClassPath, boolean noParameterRegisters,
boolean useSequentialLabels, boolean outputDebugInfo) boolean useLocalsDirective, boolean useSequentialLabels,
boolean outputDebugInfo, boolean verboseRegisterInfo)
{ {
baksmali.noParameterRegisters = noParameterRegisters; baksmali.noParameterRegisters = noParameterRegisters;
baksmali.useLocalsDirective = useLocalsDirective; baksmali.useLocalsDirective = useLocalsDirective;
baksmali.useSequentialLabels = useSequentialLabels; baksmali.useSequentialLabels = useSequentialLabels;
baksmali.outputDebugInfo = outputDebugInfo; baksmali.outputDebugInfo = outputDebugInfo;
baksmali.verboseRegisterInfo = verboseRegisterInfo;
baksmali.bootClassPath = bootClassPath;
if (verboseRegisterInfo) {
ClassPath.InitializeClassPath(bootClassPath.split(":"), dexFile);
}
if (deodexerant != null) { if (deodexerant != null) {
baksmali.deodexUtil = new DeodexUtil(deodexerant); baksmali.deodexUtil = new DeodexUtil(deodexerant);

View File

@ -75,13 +75,14 @@ public class main {
boolean useLocalsDirective = false; boolean useLocalsDirective = false;
boolean useSequentialLabels = false; boolean useSequentialLabels = false;
boolean outputDebugInfo = true; boolean outputDebugInfo = true;
boolean verboseRegisterInfo = false;
String outputDirectory = "out"; String outputDirectory = "out";
String dumpFileName = null; String dumpFileName = null;
String outputDexFileName = null; String outputDexFileName = null;
String inputDexFileName = null; String inputDexFileName = null;
String deodexerantHost = null; String deodexerantHost = null;
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
int deodexerantPort = 0; int deodexerantPort = 0;
String[] remainingArgs = commandLine.getArgs(); String[] remainingArgs = commandLine.getArgs();
@ -145,6 +146,14 @@ public class main {
outputDebugInfo = false; outputDebugInfo = false;
} }
if (commandLine.hasOption("r")) {
verboseRegisterInfo = true;
}
if (commandLine.hasOption("c")) {
bootClassPath = commandLine.getOptionValue("c");
}
if (commandLine.hasOption("x")) { if (commandLine.hasOption("x")) {
String deodexerantAddress = commandLine.getOptionValue("x"); String deodexerantAddress = commandLine.getOptionValue("x");
String[] parts = deodexerantAddress.split(":"); String[] parts = deodexerantAddress.split(":");
@ -200,8 +209,8 @@ public class main {
} }
if (disassemble) { if (disassemble) {
baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, noParameterRegisters, baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, bootClassPath, noParameterRegisters,
useLocalsDirective, useSequentialLabels, outputDebugInfo); useLocalsDirective, useSequentialLabels, outputDebugInfo, verboseRegisterInfo);
} }
if ((doDump || write) && !dexFile.isOdex()) { if ((doDump || write) && !dexFile.isOdex()) {
@ -310,6 +319,16 @@ public class main {
.withDescription("don't write out debug info (.local, .param, .line, etc.)") .withDescription("don't write out debug info (.local, .param, .line, etc.)")
.create("b"); .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(versionOption);
options.addOption(helpOption); options.addOption(helpOption);
options.addOption(dumpOption); options.addOption(dumpOption);
@ -323,5 +342,7 @@ public class main {
options.addOption(useLocalsOption); options.addOption(useLocalsOption);
options.addOption(sequentialLabelsOption); options.addOption(sequentialLabelsOption);
options.addOption(noDebugInfoOption); options.addOption(noDebugInfoOption);
options.addOption(verboseRegisterInfoOption);
options.addOption(classPathOption);
} }
} }

View File

@ -435,3 +435,8 @@ AnnotationElement(Name, Value) ::=
<< <<
<Name> = <Value> <Name> = <Value>
>> >>
Comment(Comment) ::=
<<
#<Comment>
>>

View File

@ -12,7 +12,7 @@ public class AnalyzedInstruction {
/** /**
* The actual instruction * 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 * 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.instruction = instruction;
this.instructionIndex = instructionIndex; this.instructionIndex = instructionIndex;
this.postRegisterMap = new RegisterType[registerCount]; 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() { public int getInstructionIndex() {
return instructionIndex; return instructionIndex;
} }
public int getPredecessorCount() {
return predecessors.size();
}
protected void addPredecessor(AnalyzedInstruction predecessor) { protected void addPredecessor(AnalyzedInstruction predecessor) {
predecessors.add(predecessor); predecessors.add(predecessor);
} }
@ -104,7 +112,8 @@ public class AnalyzedInstruction {
} }
protected boolean isInvokeInit() { 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; return false;
} }
@ -115,7 +124,7 @@ public class AnalyzedInstruction {
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM; assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
MethodIdItem method = (MethodIdItem)item; MethodIdItem method = (MethodIdItem)item;
if (!method.getMethodName().equals("<init>")) { if (!method.getMethodName().getStringValue().equals("<init>")) {
return false; return false;
} }
@ -175,6 +184,14 @@ public class AnalyzedInstruction {
return ((SingleRegisterInstruction)instruction).getRegisterA(); return ((SingleRegisterInstruction)instruction).getRegisterA();
} }
public int getRegisterCount() {
return postRegisterMap.length;
}
public RegisterType getPostInstructionRegisterType(int registerNumber) {
return postRegisterMap[registerNumber];
}
public RegisterType getPreInstructionRegisterType(int registerNumber) { public RegisterType getPreInstructionRegisterType(int registerNumber) {
//if the specific register is not a destination register, then the stored post-instruction register type will //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. //be the same as the pre-instruction regsiter type, so we can use that.

View File

@ -15,35 +15,32 @@ public class ClassPath {
private static ClassPath theClassPath = null; private static ClassPath theClassPath = null;
private final HashMap<String, ClassDef> classDefs; 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) { public static void InitializeClassPath(String[] bootClassPath, DexFile dexFile) {
if (theClassPath != null) { if (theClassPath != null) {
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times"); 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) { if (bootClassPath == null || bootClassPath.length == 0) {
throw new ExceptionWithContext("No BOOTCLASSPATH entries were given"); throw new ExceptionWithContext("No BOOTCLASSPATH entries were given");
} }
classDefs = new HashMap<String, ClassDef>();
for (String bootClassPathEntry: bootClassPath) { for (String bootClassPathEntry: bootClassPath) {
loadBootClassPath(bootClassPathEntry); loadBootClassPath(bootClassPathEntry);
} }
loadDexFile(dexFile); 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"}) { for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
ClassDef classDef = new PrimitiveClassDef(primitiveType); ClassDef classDef = new PrimitiveClassDef(primitiveType);
classDefs.put(primitiveType, classDef); 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?) //TODO: need to check if the class already exists. (and if so, what to do about it?)
ClassDef classDef = new ClassDef(classDefItem); ClassDef classDef = new ClassDef(classDefItem);
classDefs.put(classDef.getClassType(), classDef); 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 String classType;
private final ClassDef superclass; private final ClassDef superclass;
/** /**
@ -329,7 +330,7 @@ public class ClassPath {
} }
} }
protected ClassDef(ClassDefItem classDefItem) { protected ClassDef(ClassDefItem classDefItem) {
classType = classDefItem.getClassType().getTypeDescriptor(); classType = classDefItem.getClassType().getTypeDescriptor();
isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0; isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
@ -406,23 +407,23 @@ public class ClassPath {
} }
//TODO: GROT //TODO: GROT
public void dumpVtable() { /*public void dumpVtable() {
System.out.println(classType + " methods:"); System.out.println(classType + " methods:");
int i=0; int i=0;
for (String method: vtable) { for (String method: vtable) {
System.out.println(i + ":\t" + method); System.out.println(i + ":\t" + method);
i++; i++;
} }
} }*/
//TODO: GROT //TODO: GROT
public void dumpFields() { /*public void dumpFields() {
System.out.println(classType + " fields:"); System.out.println(classType + " fields:");
for (int i=0; i<instanceFields.size(); i++) { for (int i=0; i<instanceFields.size(); i++) {
int fieldOffset = instanceFields.keyAt(i); int fieldOffset = instanceFields.keyAt(i);
System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i)); System.out.println(fieldOffset + ":\t" + instanceFields.valueAt(i));
} }
} }*/
private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) { private void swap(byte[] fieldTypes, String[] fields, int position1, int position2) {
byte tempType = fieldTypes[position1]; byte tempType = fieldTypes[position1];
@ -725,5 +726,9 @@ public class ClassPath {
public int hashCode() { public int hashCode() {
return classType.hashCode(); return classType.hashCode();
} }
public int compareTo(ClassDef classDef) {
return classType.compareTo(classDef.classType);
}
} }
} }

View File

@ -31,7 +31,6 @@ public class MethodAnalyzer {
throw new IllegalArgumentException("The method has no code"); throw new IllegalArgumentException("The method has no code");
} }
this.encodedMethod = encodedMethod; this.encodedMethod = encodedMethod;
buildInstructionList();
//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
@ -57,6 +56,8 @@ public class MethodAnalyzer {
}; };
}; };
buildInstructionList();
instructionsToVerify = new BitSet(instructions.size()); 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 this is a constructor, then set the "this" register to an uninitialized reference of the current class
if ((encodedMethod.accessFlags & AccessFlags.CONSTRUCTOR.getValue()) != 0) { 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 //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."); 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, RegisterType.getRegisterType(RegisterType.Category.UninitRef,
ClassPath.getClassDef(methodIdItem.getContainingClass()))); ClassPath.getClassDef(methodIdItem.getContainingClass())));
} else { } 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"); throw new ValidationException("An <init> method must have the \"constructor\" access flag");
} }
@ -119,8 +120,8 @@ public class MethodAnalyzer {
while (!instructionsToVerify.isEmpty()) { while (!instructionsToVerify.isEmpty()) {
for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) { for(int i=instructionsToVerify.nextSetBit(0); i>=0; i=instructionsToVerify.nextSetBit(i+1)) {
analyzeInstruction(instructions.get(i));
instructionsToVerify.clear(i); instructionsToVerify.clear(i);
analyzeInstruction(instructions.valueAt(i));
} }
} }
@ -201,7 +202,7 @@ public class MethodAnalyzer {
boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType); boolean changed = analyzedInstruction.setPostRegisterType(registerNumber, registerType);
if (!changed || analyzedInstruction.setsRegister(registerNumber)) { if (!changed) {
return; return;
} }
@ -216,7 +217,7 @@ public class MethodAnalyzer {
while (!changedInstructions.isEmpty()) { while (!changedInstructions.isEmpty()) {
for (int instructionIndex=changedInstructions.nextSetBit(0); for (int instructionIndex=changedInstructions.nextSetBit(0);
instructionIndex>=0; instructionIndex>=0;
instructionIndex=changedInstructions.nextSetBit(instructionIndex)) { instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
changedInstructions.clear(instructionIndex); changedInstructions.clear(instructionIndex);
@ -242,9 +243,10 @@ public class MethodAnalyzer {
if (!successor.setsRegister(registerNumber)) { if (!successor.setsRegister(registerNumber)) {
RegisterType registerType = successor.getMergedRegisterTypeFromPredecessors(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; continue;
} }*/
if (successor.setPostRegisterType(registerNumber, registerType)) { if (successor.setPostRegisterType(registerNumber, registerType)) {
changedInstructions.set(successor.instructionIndex); changedInstructions.set(successor.instructionIndex);
@ -261,8 +263,6 @@ public class MethodAnalyzer {
assert encodedMethod.codeItem != null; assert encodedMethod.codeItem != null;
int registerCount = encodedMethod.codeItem.getRegisterCount(); int registerCount = encodedMethod.codeItem.getRegisterCount();
startOfMethod = new AnalyzedInstruction(null, -1, registerCount);
Instruction[] insns = encodedMethod.codeItem.getInstructions(); Instruction[] insns = encodedMethod.codeItem.getInstructions();
instructions = new SparseArray<AnalyzedInstruction>(insns.length); instructions = new SparseArray<AnalyzedInstruction>(insns.length);
@ -284,34 +284,36 @@ public class MethodAnalyzer {
AnalyzedInstruction[] currentExceptionHandlers = null; AnalyzedInstruction[] currentExceptionHandlers = null;
AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][]; AnalyzedInstruction[][] exceptionHandlers = new AnalyzedInstruction[insns.length][];
for (int i=0; i<instructions.size(); i++) { if (tries != null) {
AnalyzedInstruction instruction = instructions.valueAt(i); for (int i=0; i<instructions.size(); i++) {
Opcode instructionOpcode = instruction.instruction.opcode; AnalyzedInstruction instruction = instructions.valueAt(i);
Opcode instructionOpcode = instruction.instruction.opcode;
//check if we have gone past the end of the current try //check if we have gone past the end of the current try
if (currentTry != null) { if (currentTry != null) {
if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) { if (currentTry.getStartCodeAddress() + currentTry.getTryLength() <= currentCodeAddress) {
currentTry = null; currentTry = null;
triesIndex++; triesIndex++;
}
} }
}
//check if the next try is applicable yet //check if the next try is applicable yet
if (currentTry == null && triesIndex < tries.length) { if (currentTry == null && triesIndex < tries.length) {
CodeItem.TryItem tryItem = tries[triesIndex]; CodeItem.TryItem tryItem = tries[triesIndex];
if (tryItem.getStartCodeAddress() <= currentCodeAddress) { if (tryItem.getStartCodeAddress() <= currentCodeAddress) {
assert(tryItem.getStartCodeAddress() + tryItem.getTryLength() > 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 //if we're inside a try block, and the instruction can throw an exception, then add the exception handlers
//for the current instruction //for the current instruction
if (currentTry != null && instructionOpcode.canThrow()) { if (currentTry != null && instructionOpcode.canThrow()) {
exceptionHandlers[i] = currentExceptionHandlers; exceptionHandlers[i] = currentExceptionHandlers;
}
} }
} }
@ -333,8 +335,8 @@ public class MethodAnalyzer {
addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers); addPredecessorSuccessor(instruction, nextInstruction, exceptionHandlers);
} }
if (instruction instanceof OffsetInstruction) { if (instruction.instruction instanceof OffsetInstruction) {
OffsetInstruction offsetInstruction = (OffsetInstruction)instruction; OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) { if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
MultiOffsetInstruction switchDataInstruction = MultiOffsetInstruction switchDataInstruction =
@ -431,7 +433,7 @@ public class MethodAnalyzer {
case MOVE_OBJECT: case MOVE_OBJECT:
case MOVE_OBJECT_FROM16: case MOVE_OBJECT_FROM16:
case MOVE_OBJECT_16: case MOVE_OBJECT_16:
handleMove(analyzedInstruction, ReferenceCategories); handleMove(analyzedInstruction, ReferenceOrUninitCategories);
return; return;
case MOVE_RESULT: case MOVE_RESULT:
handleMoveResult(analyzedInstruction, Primitive32BitCategories); handleMoveResult(analyzedInstruction, Primitive32BitCategories);
@ -904,9 +906,12 @@ public class MethodAnalyzer {
private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of( private static final EnumSet<RegisterType.Category> Primitive32BitCategories = EnumSet.of(
RegisterType.Category.Null, RegisterType.Category.Null,
RegisterType.Category.One,
RegisterType.Category.Boolean, RegisterType.Category.Boolean,
RegisterType.Category.Byte, RegisterType.Category.Byte,
RegisterType.Category.PosByte,
RegisterType.Category.Short, RegisterType.Category.Short,
RegisterType.Category.PosShort,
RegisterType.Category.Char, RegisterType.Category.Char,
RegisterType.Category.Integer, RegisterType.Category.Integer,
RegisterType.Category.Float); RegisterType.Category.Float);
@ -923,11 +928,19 @@ public class MethodAnalyzer {
RegisterType.Category.Null, RegisterType.Category.Null,
RegisterType.Category.Reference); 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( private static final EnumSet<RegisterType.Category> ReferenceAndPrimitive32BitCategories = EnumSet.of(
RegisterType.Category.Null, RegisterType.Category.Null,
RegisterType.Category.One,
RegisterType.Category.Boolean, RegisterType.Category.Boolean,
RegisterType.Category.Byte, RegisterType.Category.Byte,
RegisterType.Category.PosByte,
RegisterType.Category.Short, RegisterType.Category.Short,
RegisterType.Category.PosShort,
RegisterType.Category.Char, RegisterType.Category.Char,
RegisterType.Category.Integer, RegisterType.Category.Integer,
RegisterType.Category.Float, RegisterType.Category.Float,
@ -1037,8 +1050,9 @@ public class MethodAnalyzer {
if (thisRegisterType.category == RegisterType.Category.UninitRef) { if (thisRegisterType.category == RegisterType.Category.UninitRef) {
throw new ValidationException("Returning from constructor without calling the superclass' <init>"); throw new ValidationException("Returning from constructor without calling the superclass' <init>");
} }
assert thisRegisterType.category == RegisterType.Category.Reference; //TODO: GROT
assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass()); //assert thisRegisterType.category == RegisterType.Category.Reference;
//assert thisRegisterType.type == ClassPath.getClassDef(encodedMethod.method.getContainingClass());
} }
private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) { private void handleReturnVoid(AnalyzedInstruction analyzedInstruction) {
@ -2285,8 +2299,8 @@ public class MethodAnalyzer {
} }
if (isInit) { if (isInit) {
if (objectRegisterType.type == methodClassDef.getSuperclass()) { if (objectRegisterType.type.getSuperclass() == methodClassDef) {
if (!encodedMethod.method.getMethodName().equals("<init>")) { if (!encodedMethod.method.getMethodName().getStringValue().equals("<init>")) {
throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " + throw new ValidationException(String.format("Cannot call %s on type %s. The object type must " +
"match the method type exactly", methodIdItem.getMethodString(), "match the method type exactly", methodIdItem.getMethodString(),
objectRegisterType.type.getClassType())); objectRegisterType.type.getClassType()));
@ -2302,44 +2316,46 @@ public class MethodAnalyzer {
} }
} }
List<TypeIdItem> parameterTypes = typeListItem.getTypes(); if (typeListItem != null) {
int parameterTypeIndex = 0; List<TypeIdItem> parameterTypes = typeListItem.getTypes();
while (!registers.pastEnd()) { int parameterTypeIndex = 0;
assert parameterTypeIndex < parameterTypes.size(); while (!registers.pastEnd()) {
RegisterType parameterType = assert parameterTypeIndex < parameterTypes.size();
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex)); RegisterType parameterType =
RegisterType.getRegisterTypeForTypeIdItem(parameterTypes.get(parameterTypeIndex));
int register = registers.getRegister(); int register = registers.getRegister();
RegisterType parameterRegisterType; RegisterType parameterRegisterType;
if (WideLowCategories.contains(parameterType.category)) { if (WideLowCategories.contains(parameterType.category)) {
parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories); parameterRegisterType = getAndCheckSourceRegister(analyzedInstruction, register, WideLowCategories);
if (!registers.moveNext()) { if (!registers.moveNext()) {
throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d", throw new ValidationException(String.format("No 2nd register specified for wide register pair v%d",
parameterTypeIndex+1)); 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) { assert parameterRegisterType != null;
throw new ValidationException(String.format("Invalid wide register pair (v%d, v%d). Registers " +
"must be consecutive.", register, nextRegister)); 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();
} }

View File

@ -222,18 +222,18 @@ public class RegisterType {
} }
Category mergedCategory = Category.mergeTable[this.category.ordinal()][type.category.ordinal()]; 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; ClassDef mergedType = null;
if (mergedCategory == Category.Reference) { if (mergedCategory == Category.Reference) {
mergedType = ClassPath.getCommonSuperclass(this.type, type.type); 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); return RegisterType.getRegisterType(mergedCategory, mergedType);
} }

View File

@ -281,9 +281,9 @@ public enum Opcode
//if the instruction sets the "hidden" result register //if the instruction sets the "hidden" result register
public static final int SETS_RESULT = 0x8; public static final int SETS_RESULT = 0x8;
//if the instruction sets the value of it's first register //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 //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 { static {
opcodesByValue = new Opcode[256]; opcodesByValue = new Opcode[256];