Add the ability to read the dependency information from an odex file, and use those dependencies as the BOOTCLASSPATH by default for odex files

git-svn-id: https://smali.googlecode.com/svn/trunk@679 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com
2010-04-03 23:01:03 +00:00
parent dfb1b8c6c0
commit 78bde01ad4
6 changed files with 192 additions and 20 deletions

View File

@ -38,6 +38,8 @@ import org.jf.dexlib.Util.SparseArray;
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ClassPath {
private static ClassPath theClassPath = null;
@ -48,21 +50,81 @@ public class ClassPath {
//This is only used while initialing the class path. It is set to null after initialization has finished.
private LinkedHashMap<String, TempClassInfo> tempClasses;
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath, String dexFilePath,
DexFile dexFile) {
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
/**
* Initialize the class path using the dependencies from an odex file
* @param classPathDirs The directories to search for boot class path files
* @param extraBootClassPathEntries any extra entries that should be added after the entries that are read
* from the odex file
* @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
*/
public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
String dexFilePath, DexFile dexFile) {
if (!dexFile.isOdex()) {
throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
}
if (theClassPath != null) {
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
}
OdexDependencies odexDependencies = dexFile.getOdexDependencies();
String[] bootClassPath = new String[odexDependencies.getDependencyCount()];
for (int i=0; i<bootClassPath.length; i++) {
String dependency = odexDependencies.getDependency(i);
if (dependency.endsWith(".odex")) {
int slashIndex = dependency.lastIndexOf("/");
if (slashIndex != -1) {
dependency = dependency.substring(slashIndex+1);
}
} else if (dependency.endsWith("@classes.dex")) {
Matcher m = dalvikCacheOdexPattern.matcher(dependency);
if (!m.find()) {
throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
}
dependency = m.group(1);
} else {
throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
}
bootClassPath[i] = dependency;
}
theClassPath = new ClassPath();
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
}
/**
* Initialize the class path using the given boot class path entries
* @param classPathDirs The directories to search for boot class path files
* @param bootClassPath A list of the boot class path entries to search for and load
* @param dexFilePath The path of the dex file (used for error reporting purposes only)
* @param dexFile the DexFile to load
*/
public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile) {
if (theClassPath != null) {
throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
}
theClassPath = new ClassPath();
theClassPath.initClassPath(classPathDirs, bootClassPath, dexFilePath, dexFile);
theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile);
}
private ClassPath() {
classDefs = new HashMap<String, ClassDef>();
}
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String dexFilePath, DexFile dexFile) {
private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
String dexFilePath, DexFile dexFile) {
tempClasses = new LinkedHashMap<String, TempClassInfo>();
if (bootClassPath != null) {
@ -71,6 +133,12 @@ public class ClassPath {
}
}
if (extraBootClassPathEntries != null) {
for (String bootClassPathEntry: extraBootClassPathEntries) {
loadBootClassPath(classPathDirs, bootClassPathEntry);
}
}
if (dexFile != null) {
loadDexFile(dexFilePath, dexFile);
}

View File

@ -29,7 +29,6 @@
package org.jf.dexlib;
import org.jf.dexlib.Util.*;
import org.jf.dexlib.*;
import org.jf.dexlib.Item;
import org.jf.dexlib.StringDataItem;
@ -37,13 +36,11 @@ import java.io.*;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Collections;
import java.util.zip.Adler32;
import java.util.zip.ZipFile;
import java.util.zip.ZipException;
import java.util.zip.ZipEntry;
/**
@ -180,6 +177,7 @@ public class DexFile
*/
private boolean isOdex = false;
private OdexDependencies odexDependencies;
private int dataOffset;
private int dataSize;
@ -338,7 +336,7 @@ public class DexFile
byte[] dexMagic, odexMagic;
dexMagic = org.jf.dexlib.HeaderItem.MAGIC;
odexMagic = OdexHeaderItem.MAGIC;
odexMagic = OdexHeader.MAGIC;
boolean isDex = true;
this.isOdex = true;
@ -354,9 +352,25 @@ public class DexFile
if (isOdex) {
byte[] odexHeaderBytes = FileUtils.readStream(inputStream, 40);
Input odexHeaderIn = new ByteArrayInput(odexHeaderBytes);
OdexHeaderItem odexHeader = new OdexHeaderItem(odexHeaderIn);
OdexHeader odexHeader = new OdexHeader(odexHeaderIn);
int dependencySkip = odexHeader.depsOffset - odexHeader.dexOffset - odexHeader.dexLength;
if (dependencySkip < 0) {
throw new ExceptionWithContext("Unexpected placement of the odex dependency data");
}
if (odexHeader.dexOffset > 40) {
FileUtils.readStream(inputStream, odexHeader.dexOffset - 40);
}
in = new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.dexLength));
if (dependencySkip > 0) {
FileUtils.readStream(inputStream, dependencySkip);
}
odexDependencies = new OdexDependencies(
new ByteArrayInput(FileUtils.readStream(inputStream, odexHeader.depsLength)));
} else if (isDex) {
in = new ByteArrayInput(FileUtils.readStream(inputStream, (int)fileLength));
} else {
@ -515,6 +529,14 @@ public class DexFile
return this.isOdex;
}
/**
* @return an OdexDependencies object that contains the dependencies for this odex, or null if this
* DexFile represents a dex file instead of an odex file
*/
public OdexDependencies getOdexDependencies() {
return odexDependencies;
}
/**
* Get a boolean value indicating whether items in this dex file should be
* written back out "in-place", or whether the normal layout logic should be

View File

@ -25,7 +25,53 @@
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.dexlib;
import org.jf.dexlib.Util.Input;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class OdexDependencies {
public final int modificationTime;
public final int crc;
public final int dalvikBuild;
private final String[] dependencies;
private final byte[][] dependencyChecksums;
public OdexDependencies (Input in) {
modificationTime = in.readInt();
crc = in.readInt();
dalvikBuild = in.readInt();
int dependencyCount = in.readInt();
dependencies = new String[dependencyCount];
dependencyChecksums = new byte[dependencyCount][];
for (int i=0; i<dependencyCount; i++) {
int stringLength = in.readInt();
try {
dependencies[i] = new String(in.readBytes(stringLength), 0, stringLength-1, "US-ASCII");
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
dependencyChecksums[i] = in.readBytes(20);
}
}
public int getDependencyCount() {
return dependencies.length;
}
public String getDependency(int index) {
return dependencies[index];
}
public byte[] getDependencyChecksum(int index) {
return Arrays.copyOf(dependencyChecksums[index], dependencyChecksums.length);
}
}