Home | History | Annotate | Download | only in dex
      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 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