From c4948a5c4948bcc774d598aa52103bfe4023f57e Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Tue, 12 Apr 2016 13:57:45 -0700 Subject: [PATCH] Add initial support for N preview odexes --- dexlib2/OatVersions.txt | 11 ++- .../org/jf/dexlib2/dexbacked/OatFile.java | 97 +++++++++++++++---- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/dexlib2/OatVersions.txt b/dexlib2/OatVersions.txt index 8aa9ea96..6c9cd2a5 100644 --- a/dexlib2/OatVersions.txt +++ b/dexlib2/OatVersions.txt @@ -21,4 +21,13 @@ fab6788358dfb64e5c370611ddbbbffab0ed0553 - 67 6e2d5747d00697a25251d25dd33b953e54709507 - 68 (revert of 54b62480) 0747466fca310eedea5fc49e37d54f240a0b3c0f - 69 (re-commit of 54b62480) 501fd635a557645ab05f893c56e1f358e21bab82 - 70 -99170c636dfae4908b102347cfe9f92bad1881cc - 71 \ No newline at end of file +99170c636dfae4908b102347cfe9f92bad1881cc - 71 +3cfa4d05afa76e19ca99ec964b535a15c73683f0 - 72 +d9786b0e5be23ea0258405165098b4216579209c - 73 +- fast class lookup table +a4f1220c1518074db18ca1044e9201492975750b - 74 +- bootclasspath list was added +625a64aad13905d8a2454bf3cc0e874487b110d5 - 75 +- class offsets moved out to a separate table +919f5536182890d2e03f59b961acf8f7c836ff61 - 74 (revert of 625a64aa) +9bdf108885a27ba05fae8501725649574d7c491b - 75 (re-commit of 625a64aa) diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java index dbeb67ce..7b529333 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java @@ -31,6 +31,7 @@ package org.jf.dexlib2.dexbacked; +import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.dexbacked.OatFile.SymbolTable.Symbol; @@ -38,11 +39,13 @@ import org.jf.dexlib2.dexbacked.raw.HeaderItem; import org.jf.util.AbstractForwardSequentialList; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.AbstractList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -54,7 +57,7 @@ public class OatFile extends BaseDexBuffer { // These are the "known working" versions that I have manually inspected the source for. // Later version may or may not work, depending on what changed. private static final int MIN_OAT_VERSION = 56; - private static final int MAX_OAT_VERSION = 71; + private static final int MAX_OAT_VERSION = 75; public static final int UNSUPPORTED = 0; public static final int SUPPORTED = 1; @@ -148,6 +151,18 @@ public class OatFile extends BaseDexBuffer { return UNKNOWN; } + @Nonnull + public List getBootClassPath() { + if (getOatVersion() < 75) { + return ImmutableList.of(); + } + String bcp = oatHeader.getKeyValue("bootclasspath"); + if (bcp == null) { + return ImmutableList.of(); + } + return Arrays.asList(bcp.split(":")); + } + @Nonnull public List getDexFiles() { return new AbstractForwardSequentialList() { @@ -174,11 +189,21 @@ public class OatFile extends BaseDexBuffer { offset += 4; // checksum - int dexOffset = readSmallUint(offset) + oatHeader.offset; + int dexOffset = readSmallUint(offset) + oatHeader.headerOffset; offset += 4; - int classCount = readSmallUint(dexOffset + HeaderItem.CLASS_COUNT_OFFSET); - offset += 4 * classCount; + + if (getOatVersion() >= 75) { + offset += 4; // offset to class offsets table + } + if (getOatVersion() >= 73) { + offset += 4; // lookup table offset + } + if (getOatVersion() < 75) { + // prior to 75, the class offsets are included here directly + int classCount = readSmallUint(dexOffset + HeaderItem.CLASS_COUNT_OFFSET); + offset += 4 * classCount; + } index++; @@ -205,63 +230,97 @@ public class OatFile extends BaseDexBuffer { return OatFile.this.getOatVersion(); } + public OatFile getOatFile() { + return OatFile.this; + } + @Override public boolean hasOdexOpcodes() { return true; } } private class OatHeader { - private final int offset; + private final int headerOffset; public OatHeader(int offset) { - this.offset = offset; + this.headerOffset = offset; } public boolean isValid() { for (int i=0; i '9') { + if (buf[headerOffset + i] < '0' || buf[headerOffset + i] > '9') { return false; } } - return buf[offset + 7] == 0; + return buf[headerOffset + 7] == 0; } public int getVersion() { - return Integer.valueOf(new String(buf, offset + 4, 3)); + return Integer.valueOf(new String(buf, headerOffset + 4, 3)); } public int getDexFileCount() { - return readSmallUint(offset + 20); + return readSmallUint(headerOffset + 20); } public int getKeyValueStoreSize() { - int version = getVersion(); - if (version < 56) { + if (getVersion() < MIN_OAT_VERSION) { throw new IllegalStateException("Unsupported oat version"); } int fieldOffset = 17 * 4; - return readSmallUint(offset + fieldOffset); + return readSmallUint(headerOffset + fieldOffset); } public int getHeaderSize() { - int version = getVersion(); - if (version >= 56) { - return 18*4 + getKeyValueStoreSize(); - } else { + if (getVersion() < MIN_OAT_VERSION) { throw new IllegalStateException("Unsupported oat version"); } + return 18*4 + getKeyValueStoreSize(); + } + @Nullable + public String getKeyValue(@Nonnull String key) { + int size = getKeyValueStoreSize(); + + int offset = headerOffset + 18 * 4; + int endOffset = offset + size; + + while (offset < endOffset) { + int keyStartOffset = offset; + while (offset < endOffset && buf[offset] != '\0') { + offset++; + } + if (offset >= endOffset) { + throw new InvalidOatFileException("Oat file contains truncated key value store"); + } + int keyEndOffset = offset; + + String k = new String(buf, keyStartOffset, keyEndOffset - keyStartOffset); + if (k.equals(key)) { + int valueStartOffset = ++offset; + while (offset < endOffset && buf[offset] != '\0') { + offset++; + } + if (offset >= endOffset) { + throw new InvalidOatFileException("Oat file contains truncated key value store"); + } + int valueEndOffset = offset; + return new String(buf, valueStartOffset, valueEndOffset - valueStartOffset); + } + offset++; + } + return null; } public int getDexListStart() { - return offset + getHeaderSize(); + return headerOffset + getHeaderSize(); } }