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