DexBackedDexFile.java revision df443569f2c10b2cc3067e4fd98ca7388a956dd6
1/* 2 * Copyright 2012, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32package org.jf.dexlib2.dexbacked; 33 34import org.jf.dexlib2.dexbacked.raw.HeaderItem; 35import org.jf.dexlib2.dexbacked.raw.StringIdItem; 36import org.jf.dexlib2.dexbacked.raw.TypeIdItem; 37import org.jf.dexlib2.dexbacked.util.FixedSizeSet; 38import org.jf.dexlib2.iface.DexFile; 39import org.jf.dexlib2.util.AnnotatedBytes; 40import org.jf.util.ExceptionWithContext; 41import org.jf.util.Utf8Utils; 42 43import javax.annotation.Nonnull; 44import javax.annotation.Nullable; 45import java.io.IOException; 46import java.io.Writer; 47import java.util.Set; 48 49public abstract class DexBackedDexFile extends BaseDexBuffer implements DexFile { 50 public DexBackedDexFile(@Nonnull byte[] buf) { 51 super(buf); 52 } 53 54 @Nonnull public abstract String getString(int stringIndex); 55 @Nullable public abstract String getOptionalString(int stringIndex); 56 @Nonnull public abstract String getType(int typeIndex); 57 @Nullable public abstract String getOptionalType(int typeIndex); 58 59 // TODO: refactor how dex items are read 60 public abstract int getMethodIdItemOffset(int methodIndex); 61 public abstract int getProtoIdItemOffset(int protoIndex); 62 public abstract int getFieldIdItemOffset(int fieldIndex); 63 64 @Override @Nonnull public abstract DexReader readerAt(int offset); 65 66 public abstract void dumpTo(Writer out, int width) throws IOException; 67 68 public static class Impl extends DexBackedDexFile { 69 private final int stringCount; 70 private final int stringStartOffset; 71 private final int typeCount; 72 private final int typeStartOffset; 73 private final int protoCount; 74 private final int protoStartOffset; 75 private final int fieldCount; 76 private final int fieldStartOffset; 77 private final int methodCount; 78 private final int methodStartOffset; 79 private final int classCount; 80 private final int classStartOffset; 81 82 private static final int PROTO_ID_ITEM_SIZE = 12; 83 private static final int FIELD_ID_ITEM_SIZE = 8; 84 private static final int METHOD_ID_ITEM_SIZE = 8; 85 private static final int CLASS_DEF_ITEM_SIZE = 32; 86 public static final int MAP_ITEM_SIZE = 12; 87 88 public static final int FIELD_CLASS_IDX_OFFSET = 0; 89 public static final int FIELD_TYPE_IDX_OFFSET = 2; 90 public static final int FIELD_NAME_IDX_OFFSET = 4; 91 92 public static final int METHOD_CLASS_IDX_OFFSET = 0; 93 public static final int METHOD_PROTO_IDX_OFFSET = 2; 94 public static final int METHOD_NAME_IDX_OFFSET = 4; 95 96 public static final int PROTO_RETURN_TYPE_IDX_OFFSET = 4; 97 public static final int PROTO_PARAM_LIST_OFF_OFFSET = 8; 98 99 public static final int TYPE_LIST_SIZE_OFFSET = 0; 100 public static final int TYPE_LIST_LIST_OFFSET = 4; 101 102 public Impl(@Nonnull byte[] buf) { 103 super(buf); 104 105 verifyMagic(); 106 verifyEndian(); 107 stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET); 108 stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET); 109 typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET); 110 typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET); 111 protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET); 112 protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET); 113 fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET); 114 fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET); 115 methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET); 116 methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET); 117 classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET); 118 classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET); 119 } 120 121 @Nonnull 122 @Override 123 public Set<? extends DexBackedClassDef> getClasses() { 124 return new FixedSizeSet<DexBackedClassDef>() { 125 @Nonnull 126 @Override 127 public DexBackedClassDef readItem(int index) { 128 return new DexBackedClassDef(Impl.this, getClassDefItemOffset(index)); 129 } 130 131 @Override 132 public int size() { 133 return classCount; 134 } 135 }; 136 } 137 138 private void verifyMagic() { 139 outer: for (byte[] magic: HeaderItem.MAGIC_VALUES) { 140 for (int i=0; i<magic.length; i++) { 141 if (buf[i] != magic[i]) { 142 continue outer; 143 } 144 } 145 return; 146 } 147 StringBuilder sb = new StringBuilder("Invalid magic value:"); 148 for (int i=0; i<8; i++) { 149 sb.append(String.format(" %02x", buf[i])); 150 } 151 throw new ExceptionWithContext(sb.toString()); 152 } 153 154 private void verifyEndian() { 155 int endian = readInt(HeaderItem.ENDIAN_TAG_OFFSET); 156 if (endian == HeaderItem.BIG_ENDIAN_TAG) { 157 throw new ExceptionWithContext("dexlib does not currently support big endian dex files."); 158 } else if (endian != HeaderItem.LITTLE_ENDIAN_TAG) { 159 StringBuilder sb = new StringBuilder("Invalid endian tag:"); 160 for (int i=0; i<4; i++) { 161 sb.append(String.format(" %02x", buf[HeaderItem.ENDIAN_TAG_OFFSET+i])); 162 } 163 throw new ExceptionWithContext(sb.toString()); 164 } 165 } 166 167 public int getStringIdItemOffset(int stringIndex) { 168 if (stringIndex < 0 || stringIndex >= stringCount) { 169 throw new ExceptionWithContext("String index out of bounds: %d", stringIndex); 170 } 171 return stringStartOffset + stringIndex*StringIdItem.ITEM_SIZE; 172 } 173 174 public int getTypeIdItemOffset(int typeIndex) { 175 if (typeIndex < 0 || typeIndex >= typeCount) { 176 throw new ExceptionWithContext("Type index out of bounds: %d", typeIndex); 177 } 178 return typeStartOffset + typeIndex*TypeIdItem.ITEM_SIZE; 179 } 180 181 @Override 182 public int getFieldIdItemOffset(int fieldIndex) { 183 if (fieldIndex < 0 || fieldIndex >= fieldCount) { 184 throw new ExceptionWithContext("Field index out of bounds: %d", fieldIndex); 185 } 186 return fieldStartOffset + fieldIndex*FIELD_ID_ITEM_SIZE; 187 } 188 189 @Override 190 public int getMethodIdItemOffset(int methodIndex) { 191 if (methodIndex < 0 || methodIndex >= methodCount) { 192 throw new ExceptionWithContext("Method index out of bounds: %d", methodIndex); 193 } 194 return methodStartOffset + methodIndex*METHOD_ID_ITEM_SIZE; 195 } 196 197 @Override 198 public int getProtoIdItemOffset(int protoIndex) { 199 if (protoIndex < 0 || protoIndex >= protoCount) { 200 throw new ExceptionWithContext("Proto index out of bounds: %d", protoIndex); 201 } 202 return protoStartOffset + protoIndex*PROTO_ID_ITEM_SIZE; 203 } 204 205 public int getClassDefItemOffset(int classIndex) { 206 if (classIndex < 0 || classIndex >= classCount) { 207 throw new ExceptionWithContext("Class index out of bounds: %d", classIndex); 208 } 209 return classStartOffset + classIndex*CLASS_DEF_ITEM_SIZE; 210 } 211 212 public int getClassCount() { 213 return classCount; 214 } 215 216 @Override 217 @Nonnull 218 public String getString(int stringIndex) { 219 int stringOffset = getStringIdItemOffset(stringIndex); 220 int stringDataOffset = readSmallUint(stringOffset); 221 DexReader reader = readerAt(stringDataOffset); 222 int utf16Length = reader.readSmallUleb128(); 223 return Utf8Utils.utf8BytesWithUtf16LengthToString(buf, reader.getOffset(), utf16Length); 224 } 225 226 @Override 227 @Nullable 228 public String getOptionalString(int stringIndex) { 229 if (stringIndex == -1) { 230 return null; 231 } 232 return getString(stringIndex); 233 } 234 235 @Override 236 @Nonnull 237 public String getType(int typeIndex) { 238 int typeOffset = getTypeIdItemOffset(typeIndex); 239 int stringIndex = readSmallUint(typeOffset); 240 return getString(stringIndex); 241 } 242 243 @Override 244 @Nullable 245 public String getOptionalType(int typeIndex) { 246 if (typeIndex == -1) { 247 return null; 248 } 249 return getType(typeIndex); 250 } 251 252 @Override 253 @Nonnull 254 public DexReader readerAt(int offset) { 255 return new DexReader(this, offset); 256 } 257 258 public void dumpTo(Writer out, int width) throws IOException { 259 AnnotatedBytes annotatedBytes = new AnnotatedBytes(width); 260 HeaderItem.getAnnotator().annotateSection(annotatedBytes, this, 1); 261 262 annotatedBytes.annotate(0, " "); 263 StringIdItem.getAnnotator().annotateSection(annotatedBytes, this, stringCount); 264 265 annotatedBytes.annotate(0, " "); 266 TypeIdItem.getAnnotator().annotateSection(annotatedBytes, this, typeCount); 267 268 annotatedBytes.writeAnnotations(out, buf); 269 } 270 } 271} 272