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 17package com.android.dex; 18 19import java.io.IOException; 20import java.io.UnsupportedEncodingException; 21import java.util.Arrays; 22 23/** 24 * The file header and map. 25 */ 26public 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 mapList = new Section(0x1000); 40 public final Section typeLists = new Section(0x1001); 41 public final Section annotationSetRefLists = new Section(0x1002); 42 public final Section annotationSets = new Section(0x1003); 43 public final Section classDatas = new Section(0x2000); 44 public final Section codes = new Section(0x2001); 45 public final Section stringDatas = new Section(0x2002); 46 public final Section debugInfos = new Section(0x2003); 47 public final Section annotations = new Section(0x2004); 48 public final Section encodedArrays = new Section(0x2005); 49 public final Section annotationsDirectories = new Section(0x2006); 50 public final Section[] sections = { 51 header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, 52 typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas, 53 debugInfos, annotations, encodedArrays, annotationsDirectories 54 }; 55 56 public int apiLevel; 57 public int checksum; 58 public byte[] signature; 59 public int fileSize; 60 public int linkSize; 61 public int linkOff; 62 public int dataSize; 63 public int dataOff; 64 65 public TableOfContents() { 66 signature = new byte[20]; 67 } 68 69 public void readFrom(Dex dex) throws IOException { 70 readHeader(dex.open(0)); 71 readMap(dex.open(mapList.off)); 72 computeSizesFromOffsets(); 73 } 74 75 private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException { 76 byte[] magic = headerIn.readByteArray(8); 77 78 if (!DexFormat.isSupportedDexMagic(magic)) { 79 throw new DexException("Unexpected magic: " + Arrays.toString(magic)); 80 } 81 82 apiLevel = DexFormat.magicToApi(magic); 83 checksum = headerIn.readInt(); 84 signature = headerIn.readByteArray(20); 85 fileSize = headerIn.readInt(); 86 int headerSize = headerIn.readInt(); 87 if (headerSize != SizeOf.HEADER_ITEM) { 88 throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize)); 89 } 90 int endianTag = headerIn.readInt(); 91 if (endianTag != DexFormat.ENDIAN_TAG) { 92 throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag)); 93 } 94 linkSize = headerIn.readInt(); 95 linkOff = headerIn.readInt(); 96 mapList.off = headerIn.readInt(); 97 if (mapList.off == 0) { 98 throw new DexException("Cannot merge dex files that do not contain a map"); 99 } 100 stringIds.size = headerIn.readInt(); 101 stringIds.off = headerIn.readInt(); 102 typeIds.size = headerIn.readInt(); 103 typeIds.off = headerIn.readInt(); 104 protoIds.size = headerIn.readInt(); 105 protoIds.off = headerIn.readInt(); 106 fieldIds.size = headerIn.readInt(); 107 fieldIds.off = headerIn.readInt(); 108 methodIds.size = headerIn.readInt(); 109 methodIds.off = headerIn.readInt(); 110 classDefs.size = headerIn.readInt(); 111 classDefs.off = headerIn.readInt(); 112 dataSize = headerIn.readInt(); 113 dataOff = headerIn.readInt(); 114 } 115 116 private void readMap(Dex.Section in) throws IOException { 117 int mapSize = in.readInt(); 118 Section previous = null; 119 for (int i = 0; i < mapSize; i++) { 120 short type = in.readShort(); 121 in.readShort(); // unused 122 Section section = getSection(type); 123 int size = in.readInt(); 124 int offset = in.readInt(); 125 126 if ((section.size != 0 && section.size != size) 127 || (section.off != -1 && section.off != offset)) { 128 throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type)); 129 } 130 131 section.size = size; 132 section.off = offset; 133 134 if (previous != null && previous.off > section.off) { 135 throw new DexException("Map is unsorted at " + previous + ", " + section); 136 } 137 138 previous = section; 139 } 140 Arrays.sort(sections); 141 } 142 143 public void computeSizesFromOffsets() { 144 int end = dataOff + dataSize; 145 for (int i = sections.length - 1; i >= 0; i--) { 146 Section section = sections[i]; 147 if (section.off == -1) { 148 continue; 149 } 150 if (section.off > end) { 151 throw new DexException("Map is unsorted at " + section); 152 } 153 section.byteCount = end - section.off; 154 end = section.off; 155 } 156 } 157 158 private Section getSection(short type) { 159 for (Section section : sections) { 160 if (section.type == type) { 161 return section; 162 } 163 } 164 throw new IllegalArgumentException("No such map item: " + type); 165 } 166 167 public void writeHeader(Dex.Section out, int api) throws IOException { 168 out.write(DexFormat.apiToMagic(api).getBytes("UTF-8")); 169 out.writeInt(checksum); 170 out.write(signature); 171 out.writeInt(fileSize); 172 out.writeInt(SizeOf.HEADER_ITEM); 173 out.writeInt(DexFormat.ENDIAN_TAG); 174 out.writeInt(linkSize); 175 out.writeInt(linkOff); 176 out.writeInt(mapList.off); 177 out.writeInt(stringIds.size); 178 out.writeInt(stringIds.off); 179 out.writeInt(typeIds.size); 180 out.writeInt(typeIds.off); 181 out.writeInt(protoIds.size); 182 out.writeInt(protoIds.off); 183 out.writeInt(fieldIds.size); 184 out.writeInt(fieldIds.off); 185 out.writeInt(methodIds.size); 186 out.writeInt(methodIds.off); 187 out.writeInt(classDefs.size); 188 out.writeInt(classDefs.off); 189 out.writeInt(dataSize); 190 out.writeInt(dataOff); 191 } 192 193 public void writeMap(Dex.Section out) throws IOException { 194 int count = 0; 195 for (Section section : sections) { 196 if (section.exists()) { 197 count++; 198 } 199 } 200 201 out.writeInt(count); 202 for (Section section : sections) { 203 if (section.exists()) { 204 out.writeShort(section.type); 205 out.writeShort((short) 0); 206 out.writeInt(section.size); 207 out.writeInt(section.off); 208 } 209 } 210 } 211 212 public static class Section implements Comparable<Section> { 213 public final short type; 214 public int size = 0; 215 public int off = -1; 216 public int byteCount = 0; 217 218 public Section(int type) { 219 this.type = (short) type; 220 } 221 222 public boolean exists() { 223 return size > 0; 224 } 225 226 public int compareTo(Section section) { 227 if (off != section.off) { 228 return off < section.off ? -1 : 1; 229 } 230 return 0; 231 } 232 233 @Override public String toString() { 234 return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size); 235 } 236 } 237} 238