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