1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dex; 18 19 import java.io.IOException; 20 import java.io.UnsupportedEncodingException; 21 import java.util.Arrays; 22 23 /** 24 * The file header and map. 25 */ 26 public final class TableOfContents { 27 28 /* 29 * TODO: factor out ID constants. 30 */ 31 32 public final Section header = new Section(0x0000); 33 public final Section stringIds = new Section(0x0001); 34 public final Section typeIds = new Section(0x0002); 35 public final Section protoIds = new Section(0x0003); 36 public final Section fieldIds = new Section(0x0004); 37 public final Section methodIds = new Section(0x0005); 38 public final Section classDefs = new Section(0x0006); 39 public final Section callSiteIds = new Section(0x0007); 40 public final Section methodHandles = new Section(0x0008); 41 public final Section mapList = new Section(0x1000); 42 public final Section typeLists = new Section(0x1001); 43 public final Section annotationSetRefLists = new Section(0x1002); 44 public final Section annotationSets = new Section(0x1003); 45 public final Section classDatas = new Section(0x2000); 46 public final Section codes = new Section(0x2001); 47 public final Section stringDatas = new Section(0x2002); 48 public final Section debugInfos = new Section(0x2003); 49 public final Section annotations = new Section(0x2004); 50 public final Section encodedArrays = new Section(0x2005); 51 public final Section annotationsDirectories = new Section(0x2006); 52 public final Section[] sections = { 53 header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds, 54 methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes, 55 stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories 56 }; 57 58 public int apiLevel; 59 public int checksum; 60 public byte[] signature; 61 public int fileSize; 62 public int linkSize; 63 public int linkOff; 64 public int dataSize; 65 public int dataOff; 66 TableOfContents()67 public TableOfContents() { 68 signature = new byte[20]; 69 } 70 readFrom(Dex dex)71 public void readFrom(Dex dex) throws IOException { 72 readHeader(dex.open(0)); 73 readMap(dex.open(mapList.off)); 74 computeSizesFromOffsets(); 75 } 76 readHeader(Dex.Section headerIn)77 private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException { 78 byte[] magic = headerIn.readByteArray(8); 79 80 if (!DexFormat.isSupportedDexMagic(magic)) { 81 String msg = 82 String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, " 83 + "0x%02x, 0x%02x, 0x%02x, 0x%02x]", 84 magic[0], magic[1], magic[2], magic[3], 85 magic[4], magic[5], magic[6], magic[7]); 86 throw new DexException(msg); 87 } 88 89 apiLevel = DexFormat.magicToApi(magic); 90 checksum = headerIn.readInt(); 91 signature = headerIn.readByteArray(20); 92 fileSize = headerIn.readInt(); 93 int headerSize = headerIn.readInt(); 94 if (headerSize != SizeOf.HEADER_ITEM) { 95 throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); 96 } 97 int endianTag = headerIn.readInt(); 98 if (endianTag != DexFormat.ENDIAN_TAG) { 99 throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); 100 } 101 linkSize = headerIn.readInt(); 102 linkOff = headerIn.readInt(); 103 mapList.off = headerIn.readInt(); 104 if (mapList.off == 0) { 105 throw new DexException("Cannot merge dex files that do not contain a map"); 106 } 107 stringIds.size = headerIn.readInt(); 108 stringIds.off = headerIn.readInt(); 109 typeIds.size = headerIn.readInt(); 110 typeIds.off = headerIn.readInt(); 111 protoIds.size = headerIn.readInt(); 112 protoIds.off = headerIn.readInt(); 113 fieldIds.size = headerIn.readInt(); 114 fieldIds.off = headerIn.readInt(); 115 methodIds.size = headerIn.readInt(); 116 methodIds.off = headerIn.readInt(); 117 classDefs.size = headerIn.readInt(); 118 classDefs.off = headerIn.readInt(); 119 dataSize = headerIn.readInt(); 120 dataOff = headerIn.readInt(); 121 } 122 readMap(Dex.Section in)123 private void readMap(Dex.Section in) throws IOException { 124 int mapSize = in.readInt(); 125 Section previous = null; 126 for (int i = 0; i < mapSize; i++) { 127 short type = in.readShort(); 128 in.readShort(); // unused 129 Section section = getSection(type); 130 int size = in.readInt(); 131 int offset = in.readInt(); 132 133 if ((section.size != 0 && section.size != size) 134 || (section.off != -1 && section.off != offset)) { 135 throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); 136 } 137 138 section.size = size; 139 section.off = offset; 140 141 if (previous != null && previous.off > section.off) { 142 throw new DexException("Map is unsorted at " + previous + ", " + section); 143 } 144 145 previous = section; 146 } 147 Arrays.sort(sections); 148 } 149 computeSizesFromOffsets()150 public void computeSizesFromOffsets() { 151 int end = dataOff + dataSize; 152 for (int i = sections.length - 1; i >= 0; i--) { 153 Section section = sections[i]; 154 if (section.off == -1) { 155 continue; 156 } 157 if (section.off > end) { 158 throw new DexException("Map is unsorted at " + section); 159 } 160 section.byteCount = end - section.off; 161 end = section.off; 162 } 163 } 164 getSection(short type)165 private Section getSection(short type) { 166 for (Section section : sections) { 167 if (section.type == type) { 168 return section; 169 } 170 } 171 throw new IllegalArgumentException("No such map item: " + type); 172 } 173 writeHeader(Dex.Section out, int api)174 public void writeHeader(Dex.Section out, int api) throws IOException { 175 out.write(DexFormat.apiToMagic(api).getBytes("UTF-8")); 176 out.writeInt(checksum); 177 out.write(signature); 178 out.writeInt(fileSize); 179 out.writeInt(SizeOf.HEADER_ITEM); 180 out.writeInt(DexFormat.ENDIAN_TAG); 181 out.writeInt(linkSize); 182 out.writeInt(linkOff); 183 out.writeInt(mapList.off); 184 out.writeInt(stringIds.size); 185 out.writeInt(stringIds.off); 186 out.writeInt(typeIds.size); 187 out.writeInt(typeIds.off); 188 out.writeInt(protoIds.size); 189 out.writeInt(protoIds.off); 190 out.writeInt(fieldIds.size); 191 out.writeInt(fieldIds.off); 192 out.writeInt(methodIds.size); 193 out.writeInt(methodIds.off); 194 out.writeInt(classDefs.size); 195 out.writeInt(classDefs.off); 196 out.writeInt(dataSize); 197 out.writeInt(dataOff); 198 } 199 writeMap(Dex.Section out)200 public void writeMap(Dex.Section out) throws IOException { 201 int count = 0; 202 for (Section section : sections) { 203 if (section.exists()) { 204 count++; 205 } 206 } 207 208 out.writeInt(count); 209 for (Section section : sections) { 210 if (section.exists()) { 211 out.writeShort(section.type); 212 out.writeShort((short) 0); 213 out.writeInt(section.size); 214 out.writeInt(section.off); 215 } 216 } 217 } 218 219 public static class Section implements Comparable<Section> { 220 public final short type; 221 public int size = 0; 222 public int off = -1; 223 public int byteCount = 0; 224 Section(int type)225 public Section(int type) { 226 this.type = (short) type; 227 } 228 exists()229 public boolean exists() { 230 return size > 0; 231 } 232 233 @Override compareTo(Section section)234 public int compareTo(Section section) { 235 if (off != section.off) { 236 return off < section.off ? -1 : 1; 237 } 238 return 0; 239 } 240 241 @Override toString()242 public String toString() { 243 return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); 244 } 245 } 246 } 247