mirror of
https://github.com/revanced/smali.git
synced 2025-05-23 18:16:23 +02:00
Simplify deodexing
This makes it possible to deodex an odex file using only the dependencies listed in the odex file itself. Adding extra dependencies via the -c option should no longer be needed.
This commit is contained in:
parent
5b4073a85c
commit
343df2f456
@ -290,6 +290,15 @@ public class MethodDefinition {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean needsAnalyzed() {
|
||||||
|
for (Instruction instruction: encodedMethod.codeItem.getInstructions()) {
|
||||||
|
if (instruction.opcode.odexOnly()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private List<MethodItem> getMethodItems() {
|
private List<MethodItem> getMethodItems() {
|
||||||
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
||||||
|
|
||||||
@ -297,7 +306,8 @@ public class MethodDefinition {
|
|||||||
return methodItems;
|
return methodItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (baksmali.registerInfo != 0 || baksmali.deodex || baksmali.verify) {
|
if ((baksmali.registerInfo != 0) || baksmali.verify ||
|
||||||
|
(baksmali.deodex && needsAnalyzed())) {
|
||||||
addAnalyzedInstructionMethodItems(methodItems);
|
addAnalyzedInstructionMethodItems(methodItems);
|
||||||
} else {
|
} else {
|
||||||
addInstructionMethodItems(methodItems);
|
addInstructionMethodItems(methodItems);
|
||||||
|
@ -75,16 +75,6 @@ public class baksmali {
|
|||||||
baksmali.bootClassPath = bootClassPath;
|
baksmali.bootClassPath = bootClassPath;
|
||||||
baksmali.verify = verify;
|
baksmali.verify = verify;
|
||||||
|
|
||||||
ClassPath.ClassPathErrorHandler classPathErrorHandler = null;
|
|
||||||
if (ignoreErrors) {
|
|
||||||
classPathErrorHandler = new ClassPath.ClassPathErrorHandler() {
|
|
||||||
public void ClassPathError(String className, Exception ex) {
|
|
||||||
System.err.println(String.format("Skipping %s", className));
|
|
||||||
ex.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (registerInfo != 0 || deodex || verify) {
|
if (registerInfo != 0 || deodex || verify) {
|
||||||
try {
|
try {
|
||||||
String[] extraBootClassPathArray = null;
|
String[] extraBootClassPathArray = null;
|
||||||
@ -101,15 +91,14 @@ public class baksmali {
|
|||||||
if (extraBootClassPathArray == null && isExtJar(dexFilePath)) {
|
if (extraBootClassPathArray == null && isExtJar(dexFilePath)) {
|
||||||
extraBootClassPathArray = new String[] {"framework.jar"};
|
extraBootClassPathArray = new String[] {"framework.jar"};
|
||||||
}
|
}
|
||||||
ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile,
|
ClassPath.InitializeClassPathFromOdex(classPathDirs, extraBootClassPathArray, dexFilePath, dexFile);
|
||||||
classPathErrorHandler);
|
|
||||||
} else {
|
} else {
|
||||||
String[] bootClassPathArray = null;
|
String[] bootClassPathArray = null;
|
||||||
if (bootClassPath != null) {
|
if (bootClassPath != null) {
|
||||||
bootClassPathArray = bootClassPath.split(":");
|
bootClassPathArray = bootClassPath.split(":");
|
||||||
}
|
}
|
||||||
ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray,
|
ClassPath.InitializeClassPath(classPathDirs, bootClassPathArray, extraBootClassPathArray,
|
||||||
dexFilePath, dexFile, classPathErrorHandler);
|
dexFilePath, dexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inlineTable != null) {
|
if (inlineTable != null) {
|
||||||
@ -156,15 +145,6 @@ public class baksmali {
|
|||||||
* package name are separated by '/'
|
* package name are separated by '/'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (registerInfo != 0 || deodex || verify) {
|
|
||||||
//If we are analyzing the bytecode, make sure that this class is loaded into the ClassPath. If it isn't
|
|
||||||
//then there was some error while loading it, and we should skip it
|
|
||||||
ClassPath.ClassDef classDef = ClassPath.getClassDef(classDefItem.getClassType(), false);
|
|
||||||
if (classDef == null || classDef instanceof ClassPath.UnresolvedClassDef) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String classDescriptor = classDefItem.getClassType().getTypeDescriptor();
|
String classDescriptor = classDefItem.getClassType().getTypeDescriptor();
|
||||||
|
|
||||||
//validate that the descriptor is formatted like we expect
|
//validate that the descriptor is formatted like we expect
|
||||||
@ -206,6 +186,7 @@ public class baksmali {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
|
System.err.println("\n\nError occured while disassembling class " + classDescriptor.replace('/', '.') + " - skipping class");
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
|
smaliFile.delete();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,8 @@ import org.jf.dexlib.Util.AccessFlags;
|
|||||||
import org.jf.dexlib.Util.ExceptionWithContext;
|
import org.jf.dexlib.Util.ExceptionWithContext;
|
||||||
import org.jf.dexlib.Util.SparseArray;
|
import org.jf.dexlib.Util.SparseArray;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@ -45,19 +47,13 @@ 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 ClassDef javaLangObjectClassDef; //Ljava/lang/Object;
|
protected ClassDef javaLangObjectClassDef; //cached ClassDef for Ljava/lang/Object;
|
||||||
|
|
||||||
//This is only used while initialing the class path. It is set to null after initialization has finished.
|
|
||||||
private LinkedHashMap<String, TempClassInfo> tempClasses;
|
|
||||||
|
|
||||||
|
// Contains the classes that we haven't loaded yet
|
||||||
|
private HashMap<String, UnresolvedClassInfo> unloadedClasses;
|
||||||
|
|
||||||
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
|
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
|
||||||
|
|
||||||
|
|
||||||
public static interface ClassPathErrorHandler {
|
|
||||||
void ClassPathError(String className, Exception ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the class path using the dependencies from an odex file
|
* Initialize the class path using the dependencies from an odex file
|
||||||
* @param classPathDirs The directories to search for boot class path files
|
* @param classPathDirs The directories to search for boot class path files
|
||||||
@ -65,12 +61,9 @@ public class ClassPath {
|
|||||||
* from the odex file
|
* from the odex file
|
||||||
* @param dexFilePath The path of the dex file (used for error reporting purposes only)
|
* @param dexFilePath The path of the dex file (used for error reporting purposes only)
|
||||||
* @param dexFile The DexFile to load - it must represents an odex file
|
* @param dexFile The DexFile to load - it must represents an odex file
|
||||||
* @param errorHandler a ClassPathErrorHandler object to receive and handle any errors that occur while loading
|
|
||||||
* classes
|
|
||||||
*/
|
*/
|
||||||
public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
|
public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
|
||||||
String dexFilePath, DexFile dexFile,
|
String dexFilePath, DexFile dexFile) {
|
||||||
ClassPathErrorHandler errorHandler) {
|
|
||||||
if (!dexFile.isOdex()) {
|
if (!dexFile.isOdex()) {
|
||||||
throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
|
throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
|
||||||
}
|
}
|
||||||
@ -107,8 +100,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
theClassPath = new ClassPath();
|
theClassPath = new ClassPath();
|
||||||
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
|
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
|
||||||
errorHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,15 +113,13 @@ public class ClassPath {
|
|||||||
* classes
|
* classes
|
||||||
*/
|
*/
|
||||||
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
|
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
|
||||||
String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile,
|
String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile) {
|
||||||
ClassPathErrorHandler errorHandler) {
|
|
||||||
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();
|
theClassPath = new ClassPath();
|
||||||
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
|
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
|
||||||
errorHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassPath() {
|
private ClassPath() {
|
||||||
@ -137,8 +127,8 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
|
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
|
||||||
String dexFilePath, DexFile dexFile, ClassPathErrorHandler errorHandler) {
|
String dexFilePath, DexFile dexFile) {
|
||||||
tempClasses = new LinkedHashMap<String, TempClassInfo>();
|
unloadedClasses = new LinkedHashMap<String, UnresolvedClassInfo>();
|
||||||
|
|
||||||
if (bootClassPath != null) {
|
if (bootClassPath != null) {
|
||||||
for (String bootClassPathEntry: bootClassPath) {
|
for (String bootClassPathEntry: bootClassPath) {
|
||||||
@ -156,32 +146,12 @@ public class ClassPath {
|
|||||||
loadDexFile(dexFilePath, dexFile);
|
loadDexFile(dexFilePath, dexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
javaLangObjectClassDef = getClassDef("Ljava/lang/Object;", false);
|
||||||
for (String classType: tempClasses.keySet()) {
|
|
||||||
ClassDef classDef = null;
|
|
||||||
try {
|
|
||||||
classDef = ClassPath.loadClassDef(classType);
|
|
||||||
assert classDef != null;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
if (errorHandler != null) {
|
|
||||||
errorHandler.ClassPathError(classType, ex);
|
|
||||||
} else {
|
|
||||||
throw ExceptionWithContext.withContext(ex,
|
|
||||||
String.format("Error while loading ClassPath class %s", classType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classType.equals("Ljava/lang/Object;")) {
|
|
||||||
this.javaLangObjectClassDef = classDef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
tempClasses = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) {
|
private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) {
|
||||||
@ -240,10 +210,10 @@ public class ClassPath {
|
|||||||
private void loadDexFile(String dexFilePath, DexFile dexFile) {
|
private void loadDexFile(String dexFilePath, DexFile dexFile) {
|
||||||
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
|
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
|
||||||
try {
|
try {
|
||||||
TempClassInfo tempClassInfo = new TempClassInfo(dexFilePath, classDefItem);
|
UnresolvedClassInfo unresolvedClassInfo = new UnresolvedClassInfo(dexFilePath, classDefItem);
|
||||||
|
|
||||||
if (!tempClasses.containsKey(tempClassInfo.classType)) {
|
if (!unloadedClasses.containsKey(unresolvedClassInfo.classType)) {
|
||||||
tempClasses.put(tempClassInfo.classType, tempClassInfo);
|
unloadedClasses.put(unresolvedClassInfo.classType, unresolvedClassInfo);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s",
|
throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s",
|
||||||
@ -252,42 +222,33 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ClassNotFoundException extends ExceptionWithContext {
|
|
||||||
public ClassNotFoundException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ClassDef getClassDef(String classType) {
|
|
||||||
return getClassDef(classType, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method checks if the given class has been loaded yet. If it has, it returns the loaded ClassDef. If not,
|
* This method loads the given class (and any dependent classes, as needed), removing them from unloadedClasses
|
||||||
* then it looks up the TempClassItem for the given class and (possibly recursively) loads the ClassDef.
|
|
||||||
* @param classType the class to load
|
* @param classType the class to load
|
||||||
* @return the existing or newly loaded ClassDef object for the given class, or null if the class cannot be found
|
* @return the newly loaded ClassDef object for the given class, or null if the class cannot be found
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
private static ClassDef loadClassDef(String classType) {
|
private static ClassDef loadClassDef(String classType) {
|
||||||
ClassDef classDef = getClassDef(classType, false);
|
ClassDef classDef = null;
|
||||||
|
|
||||||
if (classDef == null) {
|
UnresolvedClassInfo classInfo = theClassPath.unloadedClasses.get(classType);
|
||||||
TempClassInfo classInfo = theClassPath.tempClasses.get(classType);
|
if (classInfo == null) {
|
||||||
if (classInfo == null) {
|
return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
classDef = new ClassDef(classInfo);
|
|
||||||
theClassPath.classDefs.put(classDef.classType, classDef);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s",
|
|
||||||
classInfo.classType, classInfo.dexFilePath));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
classDef = new ClassDef(classInfo);
|
||||||
|
theClassPath.classDefs.put(classDef.classType, classDef);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s",
|
||||||
|
classInfo.classType, classInfo.dexFilePath));
|
||||||
|
}
|
||||||
|
theClassPath.unloadedClasses.remove(classType);
|
||||||
|
|
||||||
return classDef;
|
return classDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
|
public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef) {
|
||||||
ClassDef classDef = theClassPath.classDefs.get(classType);
|
ClassDef classDef = theClassPath.classDefs.get(classType);
|
||||||
if (classDef == null) {
|
if (classDef == null) {
|
||||||
@ -295,17 +256,31 @@ public class ClassPath {
|
|||||||
if (classType.charAt(0) == '[') {
|
if (classType.charAt(0) == '[') {
|
||||||
return theClassPath.createArrayClassDef(classType);
|
return theClassPath.createArrayClassDef(classType);
|
||||||
} else {
|
} else {
|
||||||
if (createUnresolvedClassDef) {
|
try {
|
||||||
//TODO: we should output a warning
|
classDef = loadClassDef(classType);
|
||||||
return theClassPath.createUnresolvedClassDef(classType);
|
if (classDef == null) {
|
||||||
} else {
|
throw new ExceptionWithContext(
|
||||||
return null;
|
String.format("Could not find definition for class %s", classType));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
RuntimeException exWithContext = ExceptionWithContext.withContext(ex,
|
||||||
|
String.format("Error while loading ClassPath class %s", classType));
|
||||||
|
if (createUnresolvedClassDef) {
|
||||||
|
//TODO: add warning message
|
||||||
|
return theClassPath.createUnresolvedClassDef(classType);
|
||||||
|
} else {
|
||||||
|
throw exWithContext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return classDef;
|
return classDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ClassDef getClassDef(String classType) {
|
||||||
|
return getClassDef(classType, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static ClassDef getClassDef(TypeIdItem classType) {
|
public static ClassDef getClassDef(TypeIdItem classType) {
|
||||||
return getClassDef(classType.getTypeDescriptor());
|
return getClassDef(classType.getTypeDescriptor());
|
||||||
}
|
}
|
||||||
@ -322,6 +297,14 @@ public class ClassPath {
|
|||||||
return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType);
|
return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClassDef unresolvedObjectClassDef = null;
|
||||||
|
public static ClassDef getUnresolvedObjectClassDef() {
|
||||||
|
if (unresolvedObjectClassDef == null) {
|
||||||
|
unresolvedObjectClassDef = new UnresolvedClassDef("Ljava/lang/Object;");
|
||||||
|
}
|
||||||
|
return unresolvedObjectClassDef;
|
||||||
|
}
|
||||||
|
|
||||||
private ClassDef createUnresolvedClassDef(String classType) {
|
private ClassDef createUnresolvedClassDef(String classType) {
|
||||||
assert classType.charAt(0) == 'L';
|
assert classType.charAt(0) == 'L';
|
||||||
|
|
||||||
@ -449,7 +432,7 @@ public class ClassPath {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
elementClass = ClassPath.getClassDef(arrayClassType.substring(i));
|
elementClass = ClassPath.getClassDef(arrayClassType.substring(i));
|
||||||
} catch (ClassNotFoundException ex) {
|
} catch (Exception ex) {
|
||||||
throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType);
|
throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType);
|
||||||
}
|
}
|
||||||
arrayDimensions = i;
|
arrayDimensions = i;
|
||||||
@ -662,7 +645,7 @@ public class ClassPath {
|
|||||||
} else /*if (classFlavor == UnresolvedClassDef)*/ {
|
} else /*if (classFlavor == UnresolvedClassDef)*/ {
|
||||||
assert classType.charAt(0) == 'L';
|
assert classType.charAt(0) == 'L';
|
||||||
this.classType = classType;
|
this.classType = classType;
|
||||||
this.superclass = ClassPath.theClassPath.javaLangObjectClassDef;
|
this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||||
implementedInterfaces = new TreeSet<ClassDef>();
|
implementedInterfaces = new TreeSet<ClassDef>();
|
||||||
isInterface = false;
|
isInterface = false;
|
||||||
|
|
||||||
@ -677,7 +660,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ClassDef(TempClassInfo classInfo) {
|
protected ClassDef(UnresolvedClassInfo classInfo) {
|
||||||
classType = classInfo.classType;
|
classType = classInfo.classType;
|
||||||
isInterface = classInfo.isInterface;
|
isInterface = classInfo.isInterface;
|
||||||
|
|
||||||
@ -812,7 +795,7 @@ public class ClassPath {
|
|||||||
fields[position2] = tempField;
|
fields[position2] = tempField;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassDef loadSuperclass(TempClassInfo classInfo) {
|
private ClassDef loadSuperclass(UnresolvedClassInfo classInfo) {
|
||||||
if (classInfo.classType.equals("Ljava/lang/Object;")) {
|
if (classInfo.classType.equals("Ljava/lang/Object;")) {
|
||||||
if (classInfo.superclassType != null) {
|
if (classInfo.superclassType != null) {
|
||||||
throw new ExceptionWithContext("Invalid superclass " +
|
throw new ExceptionWithContext("Invalid superclass " +
|
||||||
@ -826,9 +809,12 @@ public class ClassPath {
|
|||||||
throw new ExceptionWithContext(classInfo.classType + " has no superclass");
|
throw new ExceptionWithContext(classInfo.classType + " has no superclass");
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassDef superclass = ClassPath.loadClassDef(superclassType);
|
ClassDef superclass;
|
||||||
if (superclass == null) {
|
try {
|
||||||
throw new ClassNotFoundException(String.format("Could not find superclass %s", superclassType));
|
superclass = ClassPath.getClassDef(superclassType);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw ExceptionWithContext.withContext(ex,
|
||||||
|
String.format("Could not find superclass %s", superclassType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInterface && superclass.isInterface) {
|
if (!isInterface && superclass.isInterface) {
|
||||||
@ -845,7 +831,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeSet<ClassDef> loadAllImplementedInterfaces(TempClassInfo classInfo) {
|
private TreeSet<ClassDef> loadAllImplementedInterfaces(UnresolvedClassInfo classInfo) {
|
||||||
assert classType != null;
|
assert classType != null;
|
||||||
assert classType.equals("Ljava/lang/Object;") || superclass != null;
|
assert classType.equals("Ljava/lang/Object;") || superclass != null;
|
||||||
assert classInfo != null;
|
assert classInfo != null;
|
||||||
@ -861,9 +847,12 @@ public class ClassPath {
|
|||||||
|
|
||||||
if (classInfo.interfaces != null) {
|
if (classInfo.interfaces != null) {
|
||||||
for (String interfaceType: classInfo.interfaces) {
|
for (String interfaceType: classInfo.interfaces) {
|
||||||
ClassDef interfaceDef = ClassPath.loadClassDef(interfaceType);
|
ClassDef interfaceDef;
|
||||||
if (interfaceDef == null) {
|
try {
|
||||||
throw new ClassNotFoundException(String.format("Could not find interface %s", interfaceType));
|
interfaceDef = ClassPath.getClassDef(interfaceType);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw ExceptionWithContext.withContext(ex,
|
||||||
|
String.format("Could not find interface %s", interfaceType));
|
||||||
}
|
}
|
||||||
assert interfaceDef.isInterface();
|
assert interfaceDef.isInterface();
|
||||||
implementedInterfaceSet.add(interfaceDef);
|
implementedInterfaceSet.add(interfaceDef);
|
||||||
@ -880,7 +869,7 @@ public class ClassPath {
|
|||||||
return implementedInterfaceSet;
|
return implementedInterfaceSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedHashMap<String, ClassDef> loadInterfaceTable(TempClassInfo classInfo) {
|
private LinkedHashMap<String, ClassDef> loadInterfaceTable(UnresolvedClassInfo classInfo) {
|
||||||
if (classInfo.interfaces == null) {
|
if (classInfo.interfaces == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -889,9 +878,12 @@ public class ClassPath {
|
|||||||
|
|
||||||
for (String interfaceType: classInfo.interfaces) {
|
for (String interfaceType: classInfo.interfaces) {
|
||||||
if (!interfaceTable.containsKey(interfaceType)) {
|
if (!interfaceTable.containsKey(interfaceType)) {
|
||||||
ClassDef interfaceDef = ClassPath.loadClassDef(interfaceType);
|
ClassDef interfaceDef;
|
||||||
if (interfaceDef == null) {
|
try {
|
||||||
throw new ClassNotFoundException(String.format("Could not find interface %s", interfaceType));
|
interfaceDef = ClassPath.getClassDef(interfaceType);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw ExceptionWithContext.withContext(ex,
|
||||||
|
String.format("Could not find interface %s", interfaceType));
|
||||||
}
|
}
|
||||||
interfaceTable.put(interfaceType, interfaceDef);
|
interfaceTable.put(interfaceType, interfaceDef);
|
||||||
|
|
||||||
@ -908,7 +900,7 @@ public class ClassPath {
|
|||||||
return interfaceTable;
|
return interfaceTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] loadVtable(TempClassInfo classInfo) {
|
private String[] loadVtable(UnresolvedClassInfo classInfo) {
|
||||||
//TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
|
//TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
|
||||||
List<String> virtualMethodList = new LinkedList<String>();
|
List<String> virtualMethodList = new LinkedList<String>();
|
||||||
//use a temp hash table, so that we can construct the final lookup with an appropriate
|
//use a temp hash table, so that we can construct the final lookup with an appropriate
|
||||||
@ -981,7 +973,7 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SparseArray<FieldDef> loadFields(TempClassInfo classInfo) {
|
private SparseArray<FieldDef> loadFields(UnresolvedClassInfo classInfo) {
|
||||||
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
|
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
|
||||||
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
|
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
|
||||||
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
|
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
|
||||||
@ -1175,10 +1167,10 @@ public class ClassPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In some cases, classes can reference another class (i.e. a superclass or an interface) that is in a *later*
|
* This aggregates the basic information about a class in an easy-to-use format, without requiring references
|
||||||
* boot class path entry. So we load all classes from all boot class path entries before starting to process them
|
* to any other class.
|
||||||
*/
|
*/
|
||||||
private static class TempClassInfo {
|
private static class UnresolvedClassInfo {
|
||||||
public final String dexFilePath;
|
public final String dexFilePath;
|
||||||
public final String classType;
|
public final String classType;
|
||||||
public final boolean isInterface;
|
public final boolean isInterface;
|
||||||
@ -1189,7 +1181,7 @@ public class ClassPath {
|
|||||||
public final String[] virtualMethods;
|
public final String[] virtualMethods;
|
||||||
public final String[][] instanceFields;
|
public final String[][] instanceFields;
|
||||||
|
|
||||||
public TempClassInfo(String dexFilePath, ClassDefItem classDefItem) {
|
public UnresolvedClassInfo(String dexFilePath, ClassDefItem classDefItem) {
|
||||||
this.dexFilePath = dexFilePath;
|
this.dexFilePath = dexFilePath;
|
||||||
|
|
||||||
classType = classDefItem.getClassType().getTypeDescriptor();
|
classType = classDefItem.getClassType().getTypeDescriptor();
|
||||||
|
@ -91,7 +91,11 @@ public class DeodexUtil {
|
|||||||
String methodParams = m.group(2);
|
String methodParams = m.group(2);
|
||||||
String methodRet = m.group(3);
|
String methodRet = m.group(3);
|
||||||
|
|
||||||
if (classDef.isInterface()) {
|
if (classDef instanceof ClassPath.UnresolvedClassDef) {
|
||||||
|
//if this is an unresolved class, the only way getVirtualMethod could have found a method is if the virtual
|
||||||
|
//method being looked up was a method on java.lang.Object.
|
||||||
|
classDef = ClassPath.getClassDef("Ljava/lang/Object;");
|
||||||
|
} else if (classDef.isInterface()) {
|
||||||
classDef = classDef.getSuperclass();
|
classDef = classDef.getSuperclass();
|
||||||
assert classDef != null;
|
assert classDef != null;
|
||||||
}
|
}
|
||||||
|
@ -272,9 +272,13 @@ public class RegisterType {
|
|||||||
|
|
||||||
ClassDef mergedType = null;
|
ClassDef mergedType = null;
|
||||||
if (mergedCategory == Category.Reference) {
|
if (mergedCategory == Category.Reference) {
|
||||||
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
if (this.type instanceof ClassPath.UnresolvedClassDef ||
|
||||||
}
|
type.type instanceof ClassPath.UnresolvedClassDef) {
|
||||||
if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
|
mergedType = ClassPath.getUnresolvedObjectClassDef();
|
||||||
|
} else {
|
||||||
|
mergedType = ClassPath.getCommonSuperclass(this.type, type.type);
|
||||||
|
}
|
||||||
|
} else if (mergedCategory == Category.UninitRef || mergedCategory == Category.UninitThis) {
|
||||||
if (this.category == Category.Unknown) {
|
if (this.category == Category.Unknown) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user