Add oat support in DexFileFactory

This commit is contained in:
Ben Gruver 2015-07-13 21:39:28 -07:00
parent 3ff884b1c3
commit 8920228819
10 changed files with 201 additions and 53 deletions

View File

@ -36,6 +36,7 @@ import org.jf.dexlib2.analysis.ClassPath;
import org.jf.dexlib2.analysis.InlineMethodResolver; import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.util.SyntheticAccessorResolver; import org.jf.dexlib2.util.SyntheticAccessorResolver;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -54,7 +55,7 @@ public class baksmaliOptions {
public int apiLevel = 15; public int apiLevel = 15;
public String outputDirectory = "out"; public String outputDirectory = "out";
public String dexEntry = "classes.dex"; @Nullable public String dexEntry = null;
public List<String> bootClassPathDirs = Lists.newArrayList(); public List<String> bootClassPathDirs = Lists.newArrayList();
public List<String> bootClassPathEntries = Lists.newArrayList(); public List<String> bootClassPathEntries = Lists.newArrayList();

View File

@ -31,9 +31,11 @@ package org.jf.baksmali;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
import org.jf.dexlib2.analysis.InlineMethodResolver; import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile; import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
import org.jf.util.ConsoleUtil; import org.jf.util.ConsoleUtil;
import org.jf.util.SmaliHelpFormatter; import org.jf.util.SmaliHelpFormatter;
@ -256,10 +258,20 @@ public class main {
} }
//Read in and parse the dex file //Read in and parse the dex file
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, DexBackedDexFile dexFile = null;
options.apiLevel, options.experimental); try {
dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel, options.experimental);
} catch (MultipleDexFilesException ex) {
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
"disassemble with the -e option", dexFileFile.getName()));
System.err.println("Valid entries include:");
for (OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
System.err.println(oatDexFile.filename);
}
System.exit(1);
}
if (dexFile.isOdexFile()) { if (dexFile.hasOdexOpcodes()) {
if (!options.deodex) { if (!options.deodex) {
System.err.println("Warning: You are disassembling an odex file without deodexing it. You"); System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x"); System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
@ -547,8 +559,7 @@ public class main {
"/system/framework/services.jar", "/system/framework/services.jar",
"/system/framework/apache-xml.jar", "/system/framework/apache-xml.jar",
"/system/framework/filterfw.jar"); "/system/framework/filterfw.jar");
} else if (apiLevel < 21) {
} else {
// this is correct as of api 17/4.2.2 // this is correct as of api 17/4.2.2
return Lists.newArrayList( return Lists.newArrayList(
"/system/framework/core.jar", "/system/framework/core.jar",
@ -561,6 +572,22 @@ public class main {
"/system/framework/android.policy.jar", "/system/framework/android.policy.jar",
"/system/framework/services.jar", "/system/framework/services.jar",
"/system/framework/apache-xml.jar"); "/system/framework/apache-xml.jar");
} else { // api >= 21
// TODO: verify, add new ones?
return Lists.newArrayList(
"/system/framework/core-libart.jar",
"/system/framework/conscrypt.jar",
"/system/framework/okhttp.jar",
"/system/framework/core-junit.jar",
"/system/framework/bouncycastle.jar",
"/system/framework/ext.jar",
"/system/framework/framework.jar",
"/system/framework/telephony-common.jar",
"/system/framework/voip-common.jar",
"/system/framework/ims-common.jar",
"/system/framework/mms-common.jar",
"/system/framework/android.policy.jar",
"/system/framework/apache-xml.jar");
} }
} }
} }

View File

@ -31,50 +31,55 @@
package org.jf.dexlib2; package org.jf.dexlib2;
import com.google.common.base.MoreObjects;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import org.jf.dexlib2.dexbacked.DexBackedDexFile; import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile; import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.dexbacked.OatFile;
import org.jf.dexlib2.dexbacked.OatFile.NotAnOatFileException;
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.writer.pool.DexPool; import org.jf.dexlib2.writer.pool.DexPool;
import org.jf.util.ExceptionWithContext; import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.*; import java.io.*;
import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
public final class DexFileFactory { public final class DexFileFactory {
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(String path, int api) public static DexBackedDexFile loadDexFile(@Nonnull String path, int api) throws IOException {
throws IOException {
return loadDexFile(path, api, false); return loadDexFile(path, api, false);
} }
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental) public static DexBackedDexFile loadDexFile(@Nonnull String path, int api, boolean experimental)
throws IOException { throws IOException {
return loadDexFile(new File(path), "classes.dex", Opcodes.forApi(api, experimental)); return loadDexFile(new File(path), "classes.dex", Opcodes.forApi(api, experimental));
} }
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException { public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api) throws IOException {
return loadDexFile(dexFile, api, false); return loadDexFile(dexFile, api, false);
} }
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental) public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api, boolean experimental)
throws IOException { throws IOException {
return loadDexFile(dexFile, "classes.dex", Opcodes.forApi(api, experimental)); return loadDexFile(dexFile, null, Opcodes.forApi(api, experimental));
} }
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api, public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry, int api,
boolean experimental) throws IOException { boolean experimental) throws IOException {
return loadDexFile(dexFile, dexEntry, Opcodes.forApi(api, experimental)); return loadDexFile(dexFile, dexEntry, Opcodes.forApi(api, experimental));
} }
@Nonnull @Nonnull
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry,
@Nonnull Opcodes opcodes) throws IOException { @Nonnull Opcodes opcodes) throws IOException {
ZipFile zipFile = null; ZipFile zipFile = null;
boolean isZipFile = false; boolean isZipFile = false;
@ -83,16 +88,18 @@ public final class DexFileFactory {
// if we get here, it's safe to assume we have a zip file // if we get here, it's safe to assume we have a zip file
isZipFile = true; isZipFile = true;
ZipEntry zipEntry = zipFile.getEntry(dexEntry); String zipEntryName = MoreObjects.firstNonNull(dexEntry, "classes.dex");
ZipEntry zipEntry = zipFile.getEntry(zipEntryName);
if (zipEntry == null) { if (zipEntry == null) {
throw new NoClassesDexException("zip file %s does not contain a classes.dex file", dexFile.getName()); throw new DexFileNotFound("zip file %s does not contain a %s file", dexFile.getName(), zipEntryName);
} }
long fileLength = zipEntry.getSize(); long fileLength = zipEntry.getSize();
if (fileLength < 40) { if (fileLength < 40) {
throw new ExceptionWithContext( throw new ExceptionWithContext("The %s file in %s is too small to be a valid dex file",
"The " + dexEntry + " file in %s is too small to be a valid dex file", dexFile.getName()); zipEntryName, dexFile.getName());
} else if (fileLength > Integer.MAX_VALUE) { } else if (fileLength > Integer.MAX_VALUE) {
throw new ExceptionWithContext("The " + dexEntry + " file in %s is too large to read in", dexFile.getName()); throw new ExceptionWithContext("The %s file in %s is too large to read in",
zipEntryName, dexFile.getName());
} }
byte[] dexBytes = new byte[(int)fileLength]; byte[] dexBytes = new byte[(int)fileLength];
ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes); ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes);
@ -127,30 +134,93 @@ public final class DexFileFactory {
} catch (DexBackedOdexFile.NotAnOdexFile ex) { } catch (DexBackedOdexFile.NotAnOdexFile ex) {
// just eat it // just eat it
} }
OatFile oatFile = null;
try {
oatFile = OatFile.fromInputStream(inputStream);
} catch (NotAnOatFileException ex) {
// just eat it
}
if (oatFile != null) {
if (oatFile.isSupportedVersion() == OatFile.UNSUPPORTED) {
throw new UnsupportedOatVersionException(oatFile);
}
List<OatDexFile> oatDexFiles = oatFile.getDexFiles();
if (oatDexFiles.size() == 0) {
throw new DexFileNotFound("Oat file %s contains no dex files", dexFile.getName());
}
if (dexEntry == null) {
if (oatDexFiles.size() > 1) {
throw new MultipleDexFilesException(oatFile);
}
return oatDexFiles.get(0);
} else {
// first check for an exact match
for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
if (oatDexFile.filename.equals(dexEntry)) {
return oatDexFile;
}
}
if (!dexEntry.contains("/")) {
for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
File oatEntryFile = new File(oatDexFile.filename);
if (oatEntryFile.getName().equals(dexEntry)) {
return oatDexFile;
}
}
}
throw new DexFileNotFound("oat file %s does not contain a dex file named %s",
dexFile.getName(), dexEntry);
}
}
} finally { } finally {
inputStream.close(); inputStream.close();
} }
throw new ExceptionWithContext("%s is not an apk, dex file or odex file.", dexFile.getPath()); throw new ExceptionWithContext("%s is not an apk, dex, odex or oat file.", dexFile.getPath());
} }
public static void writeDexFile(String path, DexFile dexFile) throws IOException { public static void writeDexFile(@Nonnull String path, @Nonnull DexFile dexFile) throws IOException {
DexPool.writeTo(path, dexFile); DexPool.writeTo(path, dexFile);
} }
private DexFileFactory() {} private DexFileFactory() {}
public static class NoClassesDexException extends ExceptionWithContext { public static class DexFileNotFound extends ExceptionWithContext {
public NoClassesDexException(Throwable cause) { public DexFileNotFound(@Nullable Throwable cause) {
super(cause); super(cause);
} }
public NoClassesDexException(Throwable cause, String message, Object... formatArgs) { public DexFileNotFound(@Nullable Throwable cause, @Nullable String message, Object... formatArgs) {
super(cause, message, formatArgs); super(cause, message, formatArgs);
} }
public NoClassesDexException(String message, Object... formatArgs) { public DexFileNotFound(@Nullable String message, Object... formatArgs) {
super(message, formatArgs); super(message, formatArgs);
} }
} }
public static class MultipleDexFilesException extends ExceptionWithContext {
@Nonnull public final OatFile oatFile;
public MultipleDexFilesException(@Nonnull OatFile oatFile) {
super("Oat file has multiple dex files.");
this.oatFile = oatFile;
}
}
public static class UnsupportedOatVersionException extends ExceptionWithContext {
@Nonnull public final OatFile oatFile;
public UnsupportedOatVersionException(@Nonnull OatFile oatFile) {
super("Unsupported oat version: %d", oatFile.getOatVersion());
this.oatFile = oatFile;
}
}
} }

View File

@ -39,6 +39,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.DexFileFactory.DexFileNotFound;
import org.jf.dexlib2.analysis.reflection.ReflectionClassDef; import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile; import org.jf.dexlib2.iface.DexFile;
@ -58,6 +59,7 @@ public class ClassPath {
@Nonnull private final TypeProto unknownClass; @Nonnull private final TypeProto unknownClass;
@Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap(); @Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap();
private boolean checkPackagePrivateAccess; private boolean checkPackagePrivateAccess;
public boolean isArt;
/** /**
* Creates a new ClassPath instance that can load classes from the given dex files * Creates a new ClassPath instance that can load classes from the given dex files
@ -82,15 +84,29 @@ public class ClassPath {
* Creates a new ClassPath instance that can load classes from the given dex files * Creates a new ClassPath instance that can load classes from the given dex files
* *
* @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by default * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
* default
*/ */
public ClassPath(@Nonnull Iterable<DexFile> classPath, boolean checkPackagePrivateAccess) { public ClassPath(@Nonnull Iterable<DexFile> classPath, boolean checkPackagePrivateAccess) {
this(classPath, checkPackagePrivateAccess, false);
}
/**
* Creates a new ClassPath instance that can load classes from the given dex files
*
* @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
* default
* @param isArt Whether this is ClassPath is for ART
*/
public ClassPath(@Nonnull Iterable < DexFile > classPath, boolean checkPackagePrivateAccess, boolean isArt) {
// add fallbacks for certain special classes that must be present // add fallbacks for certain special classes that must be present
Iterable<DexFile> dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses())); Iterable<DexFile> dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses()));
unknownClass = new UnknownClassProto(this); unknownClass = new UnknownClassProto(this);
loadedClasses.put(unknownClass.getType(), unknownClass); loadedClasses.put(unknownClass.getType(), unknownClass);
this.checkPackagePrivateAccess = checkPackagePrivateAccess; this.checkPackagePrivateAccess = checkPackagePrivateAccess;
this.isArt = isArt;
loadPrimitiveType("Z"); loadPrimitiveType("Z");
loadPrimitiveType("B"); loadPrimitiveType("B");
@ -223,7 +239,7 @@ public class ClassPath {
} else { } else {
try { try {
return DexFileFactory.loadDexFile(file, api, experimental); return DexFileFactory.loadDexFile(file, api, experimental);
} catch (DexFileFactory.NoClassesDexException ex) { } catch (DexFileNotFound ex) {
// ignore and continue // ignore and continue
} catch (Exception ex) { } catch (Exception ex) {
throw ExceptionWithContext.withContext(ex, throw ExceptionWithContext.withContext(ex,

View File

@ -37,13 +37,19 @@ import javax.annotation.Nonnull;
public class BaseDexBuffer { public class BaseDexBuffer {
@Nonnull /* package private */ final byte[] buf; @Nonnull /* package private */ final byte[] buf;
/* package private */ final int baseOffset;
public BaseDexBuffer(@Nonnull byte[] buf) { public BaseDexBuffer(@Nonnull byte[] buf) {
this(buf, 0);
}
public BaseDexBuffer(@Nonnull byte[] buf, int offset) {
this.buf = buf; this.buf = buf;
this.baseOffset = offset;
} }
public int readSmallUint(int offset) { public int readSmallUint(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
int result = (buf[offset] & 0xff) | int result = (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8) | ((buf[offset+1] & 0xff) << 8) |
((buf[offset+2] & 0xff) << 16) | ((buf[offset+2] & 0xff) << 16) |
@ -56,6 +62,7 @@ public class BaseDexBuffer {
public int readOptionalUint(int offset) { public int readOptionalUint(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
int result = (buf[offset] & 0xff) | int result = (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8) | ((buf[offset+1] & 0xff) << 8) |
((buf[offset+2] & 0xff) << 16) | ((buf[offset+2] & 0xff) << 16) |
@ -68,16 +75,18 @@ public class BaseDexBuffer {
public int readUshort(int offset) { public int readUshort(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
return (buf[offset] & 0xff) | return (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8); ((buf[offset+1] & 0xff) << 8);
} }
public int readUbyte(int offset) { public int readUbyte(int offset) {
return buf[offset] & 0xff; return buf[offset + baseOffset] & 0xff;
} }
public long readLong(int offset) { public long readLong(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
return (buf[offset] & 0xff) | return (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8) | ((buf[offset+1] & 0xff) << 8) |
((buf[offset+2] & 0xff) << 16) | ((buf[offset+2] & 0xff) << 16) |
@ -90,6 +99,7 @@ public class BaseDexBuffer {
public int readInt(int offset) { public int readInt(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
return (buf[offset] & 0xff) | return (buf[offset] & 0xff) |
((buf[offset+1] & 0xff) << 8) | ((buf[offset+1] & 0xff) << 8) |
((buf[offset+2] & 0xff) << 16) | ((buf[offset+2] & 0xff) << 16) |
@ -98,12 +108,13 @@ public class BaseDexBuffer {
public int readShort(int offset) { public int readShort(int offset) {
byte[] buf = this.buf; byte[] buf = this.buf;
offset += baseOffset;
return (buf[offset] & 0xff) | return (buf[offset] & 0xff) |
(buf[offset+1] << 8); (buf[offset+1] << 8);
} }
public int readByte(int offset) { public int readByte(int offset) {
return buf[offset]; return buf[baseOffset + offset];
} }
@Nonnull @Nonnull
@ -115,4 +126,8 @@ public class BaseDexBuffer {
protected byte[] getBuf() { protected byte[] getBuf() {
return buf; return buf;
} }
protected int getBaseOffset() {
return baseOffset;
}
} }

View File

@ -49,7 +49,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
public void setOffset(int offset) { this.offset = offset; } public void setOffset(int offset) { this.offset = offset; }
public int readSleb128() { public int readSleb128() {
int end = offset; int end = dexBuf.baseOffset + offset;
int currentByteValue; int currentByteValue;
int result; int result;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
@ -84,7 +84,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
} }
} }
offset = end; offset = end - dexBuf.baseOffset;
return result; return result;
} }
@ -93,7 +93,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
} }
private int readUleb128(boolean allowLarge) { private int readUleb128(boolean allowLarge) {
int end = offset; int end = dexBuf.baseOffset + offset;
int currentByteValue; int currentByteValue;
int result; int result;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
@ -129,7 +129,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
} }
} }
offset = end; offset = end - dexBuf.baseOffset;
return result; return result;
} }
@ -150,7 +150,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
* @return The unsigned value, reinterpreted as a signed int * @return The unsigned value, reinterpreted as a signed int
*/ */
public int readBigUleb128() { public int readBigUleb128() {
int end = offset; int end = dexBuf.baseOffset + offset;
int currentByteValue; int currentByteValue;
int result; int result;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
@ -179,12 +179,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
} }
} }
offset = end; offset = end - dexBuf.baseOffset;
return result; return result;
} }
public void skipUleb128() { public void skipUleb128() {
int end = offset; int end = dexBuf.baseOffset + offset;
byte currentByteValue; byte currentByteValue;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
@ -206,7 +206,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
} }
} }
offset = end; offset = end - dexBuf.baseOffset;
} }
public int readSmallUint() { public int readSmallUint() {
@ -285,7 +285,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
public int readByte(int offset) { return dexBuf.readByte(offset); } public int readByte(int offset) { return dexBuf.readByte(offset); }
public int readSizedInt(int bytes) { public int readSizedInt(int bytes) {
int o = offset; int o = dexBuf.baseOffset + offset;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
int result; int result;
@ -311,12 +311,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
default: default:
throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset); throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset);
} }
offset = o + bytes; offset = o + bytes - dexBuf.baseOffset;
return result; return result;
} }
public int readSizedSmallUint(int bytes) { public int readSizedSmallUint(int bytes) {
int o = offset; int o = dexBuf.baseOffset + offset;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
int result = 0; int result = 0;
@ -341,12 +341,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
default: default:
throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset); throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset);
} }
offset = o + bytes; offset = o + bytes - dexBuf.baseOffset;
return result; return result;
} }
public int readSizedRightExtendedInt(int bytes) { public int readSizedRightExtendedInt(int bytes) {
int o = offset; int o = dexBuf.baseOffset + offset;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
int result; int result;
@ -373,12 +373,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
throw new ExceptionWithContext( throw new ExceptionWithContext(
"Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset); "Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset);
} }
offset = o + bytes; offset = o + bytes - dexBuf.baseOffset;
return result; return result;
} }
public long readSizedRightExtendedLong(int bytes) { public long readSizedRightExtendedLong(int bytes) {
int o = offset; int o = dexBuf.baseOffset + offset;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
long result; long result;
@ -439,12 +439,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
throw new ExceptionWithContext( throw new ExceptionWithContext(
"Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset); "Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset);
} }
offset = o + bytes; offset = o + bytes - dexBuf.baseOffset;
return result; return result;
} }
public long readSizedLong(int bytes) { public long readSizedLong(int bytes) {
int o = offset; int o = dexBuf.baseOffset + offset;
byte[] buf = dexBuf.buf; byte[] buf = dexBuf.buf;
long result; long result;
@ -505,13 +505,14 @@ public class BaseDexReader<T extends BaseDexBuffer> {
throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset); throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset);
} }
offset = o + bytes; offset = o + bytes - dexBuf.baseOffset;
return result; return result;
} }
public String readString(int utf16Length) { public String readString(int utf16Length) {
int[] ret = new int[1]; int[] ret = new int[1];
String value = Utf8Utils.utf8BytesWithUtf16LengthToString(dexBuf.buf, offset, utf16Length, ret); String value = Utf8Utils.utf8BytesWithUtf16LengthToString(
dexBuf.buf, dexBuf.baseOffset + offset, utf16Length, ret);
offset += ret[0]; offset += ret[0];
return value; return value;
} }

View File

@ -62,7 +62,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
private final int classStartOffset; private final int classStartOffset;
private DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) { private DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
super(buf); super(buf, offset);
this.opcodes = opcodes; this.opcodes = opcodes;
@ -121,10 +121,16 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
return opcodes; return opcodes;
} }
// Will only be true for a dalvik-style odex file
public boolean isOdexFile() { public boolean isOdexFile() {
return false; return false;
} }
// Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
public boolean hasOdexOpcodes() {
return false;
}
@Nonnull @Nonnull
@Override @Override
public Set<? extends DexBackedClassDef> getClasses() { public Set<? extends DexBackedClassDef> getClasses() {

View File

@ -33,7 +33,6 @@ package org.jf.dexlib2.dexbacked;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.dexbacked.raw.HeaderItem;
import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem; import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem;
import org.jf.dexlib2.dexbacked.util.VariableSizeList; import org.jf.dexlib2.dexbacked.util.VariableSizeList;
@ -61,6 +60,10 @@ public class DexBackedOdexFile extends DexBackedDexFile {
return true; return true;
} }
@Override public boolean hasOdexOpcodes() {
return true;
}
public List<String> getDependencies() { public List<String> getDependencies() {
final int dexOffset = OdexHeaderItem.getDexOffset(odexBuf); final int dexOffset = OdexHeaderItem.getDexOffset(odexBuf);
final int dependencyOffset = OdexHeaderItem.getDependenciesOffset(odexBuf) - dexOffset; final int dependencyOffset = OdexHeaderItem.getDependenciesOffset(odexBuf) - dexOffset;

View File

@ -124,8 +124,12 @@ public class OatFile extends BaseDexBuffer {
return new OatFile(buf); return new OatFile(buf);
} }
public int getOatVersion() {
return oatHeader.getVersion();
}
public int isSupportedVersion() { public int isSupportedVersion() {
int version = oatHeader.getVersion(); int version = getOatVersion();
if (version < MIN_OAT_VERSION) { if (version < MIN_OAT_VERSION) {
return UNSUPPORTED; return UNSUPPORTED;
} }
@ -187,6 +191,10 @@ public class OatFile extends BaseDexBuffer {
super(opcodes, OatFile.this.buf, offset); super(opcodes, OatFile.this.buf, offset);
this.filename = filename; this.filename = filename;
} }
@Override public boolean hasOdexOpcodes() {
return true;
}
} }
private class OatHeader { private class OatHeader {

View File

@ -59,7 +59,7 @@ public class RawDexFile extends DexBackedDexFile {
@Nonnull @Nonnull
public byte[] readByteRange(int start, int length) { public byte[] readByteRange(int start, int length) {
return Arrays.copyOfRange(getBuf(), start, start+length); return Arrays.copyOfRange(getBuf(), getBaseOffset() + start, getBaseOffset() + start + length);
} }
public int getMapOffset() { public int getMapOffset() {
@ -94,6 +94,7 @@ public class RawDexFile extends DexBackedDexFile {
} }
public void writeAnnotations(@Nonnull Writer out, @Nonnull AnnotatedBytes annotatedBytes) throws IOException { public void writeAnnotations(@Nonnull Writer out, @Nonnull AnnotatedBytes annotatedBytes) throws IOException {
// TODO: need to pass in the offset
annotatedBytes.writeAnnotations(out, getBuf()); annotatedBytes.writeAnnotations(out, getBuf());
} }
} }