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