1 /*
2  * Copyright (C) 2018 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.cts.releaseparser;
18 
19 import com.android.compatibility.common.util.ReadElf;
20 import com.android.cts.releaseparser.ReleaseProto.*;
21 
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.logging.Logger;
29 
30 // Oat is embedded in an ELF file .rodata secion
31 // art/runtime/oat.h & oat.cc
32 // art/dex2oat/linker/oat_writer.h
33 public class OatParser extends FileParser {
34     // The magic values for the OAT identification.
35     private static final byte[] OAT_MAGIC = {(byte) 'o', (byte) 'a', (byte) 't', (byte) 0x0A};
36     private static final int HEADER_SIZE = 64;
37     private static String FIRST_NO_PIC_VERSION = "162\0";
38     private OatInfo.Builder mOatInfoBuilder;
39     private int mBits;
40     private String mArch;
41     private byte[] mRoData;
42     private List<String> mDependencies;
43 
OatParser(File file)44     public OatParser(File file) {
45         super(file);
46         mDependencies = null;
47     }
48 
49     @Override
getDependencies()50     public List<String> getDependencies() {
51         if (mDependencies == null) {
52             parse();
53         }
54         return mDependencies;
55     }
56 
57     @Override
getCodeId()58     public String getCodeId() {
59         if (mOatInfoBuilder == null) {
60             parse();
61         }
62         return mCodeId;
63     }
64 
65     @Override
setAdditionalInfo()66     public void setAdditionalInfo() {
67         getFileEntryBuilder().setOatInfo(getOatInfo());
68     }
69 
getOatInfo()70     public OatInfo getOatInfo() {
71         if (mOatInfoBuilder == null) {
72             parse();
73         }
74         return mOatInfoBuilder.build();
75     }
76 
parse()77     private void parse() {
78         mOatInfoBuilder = OatInfo.newBuilder();
79         try {
80             mDependencies = new ArrayList<String>();
81             ReadElf elf = ReadElf.read(getFile());
82             mOatInfoBuilder.setBits(elf.getBits());
83             mOatInfoBuilder.setArchitecture(elf.getArchitecture());
84             mRoData = elf.getRoData();
85             praseOat(mRoData);
86             mOatInfoBuilder.setValid(true);
87         } catch (Exception ex) {
88             System.err.println("Invalid OAT file:" + getFileName());
89             ex.printStackTrace(System.out);
90             mOatInfoBuilder.setValid(false);
91         }
92     }
93 
praseOat(byte[] buffer)94     private void praseOat(byte[] buffer) throws IllegalArgumentException {
95         if (buffer[0] != OAT_MAGIC[0]
96                 || buffer[1] != OAT_MAGIC[1]
97                 || buffer[2] != OAT_MAGIC[2]
98                 || buffer[3] != OAT_MAGIC[3]) {
99             String content = new String(buffer);
100             System.err.println("Invalid OAT file:" + getFileName() + " " + content);
101             throw new IllegalArgumentException("Invalid OAT MAGIC");
102         }
103 
104         int offset = 4;
105         String version = new String(Arrays.copyOfRange(buffer, offset, offset + 4));
106         mOatInfoBuilder.setVersion(version);
107         offset += 4;
108         mOatInfoBuilder.setAdler32Checksum(getIntLittleEndian(buffer, offset));
109         offset += 4;
110         mOatInfoBuilder.setInstructionSet(getIntLittleEndian(buffer, offset));
111         offset += 4;
112         mOatInfoBuilder.setInstructionSetFeaturesBitmap(getIntLittleEndian(buffer, offset));
113         offset += 4;
114         int dexFileCount = getIntLittleEndian(buffer, offset);
115         mOatInfoBuilder.setDexFileCount(dexFileCount);
116         offset += 4;
117         int dexFileOffset = getIntLittleEndian(buffer, offset);
118         mOatInfoBuilder.setOatDexFilesOffset(dexFileOffset);
119         offset += 4;
120         mOatInfoBuilder.setExecutableOffset(getIntLittleEndian(buffer, offset));
121         offset += 4;
122         mOatInfoBuilder.setInterpreterToInterpreterBridgeOffset(getIntLittleEndian(buffer, offset));
123         offset += 4;
124         mOatInfoBuilder.setInterpreterToCompiledCodeBridgeOffset(
125                 getIntLittleEndian(buffer, offset));
126         offset += 4;
127         mOatInfoBuilder.setJniDlsymLookupOffset(getIntLittleEndian(buffer, offset));
128         offset += 4;
129         mOatInfoBuilder.setQuickGenericJniTrampolineOffset(getIntLittleEndian(buffer, offset));
130         offset += 4;
131         mOatInfoBuilder.setQuickImtConflictTrampolineOffset(getIntLittleEndian(buffer, offset));
132         offset += 4;
133         mOatInfoBuilder.setQuickResolutionTrampolineOffset(getIntLittleEndian(buffer, offset));
134         offset += 4;
135         mOatInfoBuilder.setQuickToInterpreterBridgeOffset(getIntLittleEndian(buffer, offset));
136         offset += 4;
137 
138         // for backward compatibility, removed from version 162, see
139         // aosp/e0669326c0282b5b645aba75160425eef9d57617
140         if (version.compareTo(FIRST_NO_PIC_VERSION) < 0) {
141             mOatInfoBuilder.setImagePatchDelta(getIntLittleEndian(buffer, offset));
142             offset += 4;
143         }
144 
145         mOatInfoBuilder.setImageFileLocationOatChecksum(getIntLittleEndian(buffer, offset));
146         offset += 4;
147 
148         // for backward compatibility, removed from version 162, see
149         // aosp/e0669326c0282b5b645aba75160425eef9d57617
150         if (version.compareTo(FIRST_NO_PIC_VERSION) < 0) {
151             mOatInfoBuilder.setImageFileLocationOatDataBegin(getIntLittleEndian(buffer, offset));
152             offset += 4;
153         }
154         int storeSize = getIntLittleEndian(buffer, offset);
155         mOatInfoBuilder.setKeyValueStoreSize(storeSize);
156         offset += 4;
157 
158         // adds dependencies at the build time
159         // trims device & dex_bootjars from the path
160         // e.g. out/target/product/sailfish/dex_bootjars/system/framework/arm64/boot.art
161         final String dexBootJars = "/dex_bootjars/";
162         Map<String, String> kvMap = getKeyValuePairMap(buffer, offset, storeSize);
163         mOatInfoBuilder.putAllKeyValueStore(kvMap);
164         String imageLocation = kvMap.get("image-location");
165         if (imageLocation != null) {
166             for (String path : imageLocation.split(":")) {
167                 mDependencies.add(path.substring(path.indexOf(dexBootJars) + dexBootJars.length()));
168             }
169         }
170         String bootClasspath = kvMap.get("bootclasspath");
171         if (bootClasspath != null) {
172             for (String path : bootClasspath.split(":")) {
173                 mDependencies.add(path.substring(path.indexOf(dexBootJars) + dexBootJars.length()));
174             }
175         }
176         if (!mDependencies.isEmpty()) {
177             getFileEntryBuilder().addAllDependencies(mDependencies);
178         }
179 
180         StringBuilder codeIdSB = new StringBuilder();
181         // art/dex2oat/linker/oat_writer.cc OatDexFile
182         offset = dexFileOffset;
183         for (int i = 0; i < dexFileCount; i++) {
184             OatDexInfo.Builder oatDexInfoBuilder = OatDexInfo.newBuilder();
185 
186             // dex_file_location_size_
187             int length = getIntLittleEndian(buffer, offset);
188             offset += 4;
189             // dex_file_location_data_
190             oatDexInfoBuilder.setDexFileLocationData(
191                     new String(Arrays.copyOfRange(buffer, offset, offset + length)));
192             offset += length;
193 
194             // dex_file_location_checksum_
195             int dexFileLocationChecksum = getIntLittleEndian(buffer, offset);
196             offset += 4;
197             oatDexInfoBuilder.setDexFileLocationChecksum(dexFileLocationChecksum);
198             codeIdSB.append(String.format(CODE_ID_FORMAT, dexFileLocationChecksum));
199 
200             // dex_file_offset_
201             oatDexInfoBuilder.setDexFileOffset(getIntLittleEndian(buffer, offset));
202             offset += 4;
203             // lookup_table_offset_
204             oatDexInfoBuilder.setLookupTableOffset(getIntLittleEndian(buffer, offset));
205             offset += 4;
206             // uint32_t class_offsets_offset_;
207             oatDexInfoBuilder.setClassOffsetsOffset(getIntLittleEndian(buffer, offset));
208             offset += 4;
209             // uint32_t method_bss_mapping_offset_;
210             oatDexInfoBuilder.setMethodBssMappingOffset(getIntLittleEndian(buffer, offset));
211             offset += 4;
212             // uint32_t type_bss_mapping_offset_;
213             oatDexInfoBuilder.setTypeBssMappingOffset(getIntLittleEndian(buffer, offset));
214             offset += 4;
215             // uint32_t string_bss_mapping_offset_;
216             oatDexInfoBuilder.setStringBssMappingOffset(getIntLittleEndian(buffer, offset));
217             offset += 4;
218             // uint32_t dex_sections_layout_offset_;
219             oatDexInfoBuilder.setDexSectionsLayoutOffset(getIntLittleEndian(buffer, offset));
220             offset += 4;
221             mOatInfoBuilder.addOatDexInfo(oatDexInfoBuilder.build());
222         }
223         mCodeId = codeIdSB.toString();
224     }
225 
226     // as art/runtime/oat.cc GetStoreValueByKey
getKeyValuePairMap(byte[] buffer, int start, int size)227     private Map<String, String> getKeyValuePairMap(byte[] buffer, int start, int size) {
228         HashMap<String, String> keyValuePairMap = new HashMap<String, String>();
229         int currentPosition = start;
230         int end = start + size;
231         String key, value;
232         while (currentPosition < end) {
233             key = getString(buffer, currentPosition, end);
234             currentPosition += key.length() + 1;
235             value = getString(buffer, currentPosition, end);
236             currentPosition += value.length() + 1;
237             keyValuePairMap.put(key, value);
238         }
239         return keyValuePairMap;
240     }
241 
getString(byte[] buffer, int start, int end)242     private String getString(byte[] buffer, int start, int end) {
243         String str = null;
244         int currentPosition = start;
245         while (currentPosition < end) {
246             if (buffer[currentPosition] == 0x0) {
247                 str = new String(Arrays.copyOfRange(buffer, start, currentPosition));
248                 break;
249             } else {
250                 currentPosition++;
251             }
252         }
253         return str;
254     }
255 
256     @Override
getType()257     public Entry.EntryType getType() {
258         return Entry.EntryType.OAT;
259     }
260 
261     private static final String USAGE_MESSAGE =
262             "Usage: java -jar releaseparser.jar "
263                     + OatParser.class.getCanonicalName()
264                     + " [-options <parameter>]...\n"
265                     + "           to prase OAT file meta data\n"
266                     + "Options:\n"
267                     + "\t-i PATH\t The file path of the file to be parsed.\n"
268                     + "\t-of PATH\t The file path of the output file instead of printing to System.out.\n";
269 
main(String[] args)270     public static void main(String[] args) {
271         try {
272             ArgumentParser argParser = new ArgumentParser(args);
273             String fileName = argParser.getParameterElement("i", 0);
274             String outputFileName = argParser.getParameterElement("of", 0);
275 
276             File aFile = new File(fileName);
277             OatParser aParser = (OatParser) FileParser.getParser(aFile);
278             Entry fileEntry = aParser.getFileEntryBuilder().build();
279 
280             writeTextFormatMessage(outputFileName, fileEntry);
281         } catch (Exception ex) {
282             System.out.println(USAGE_MESSAGE);
283             ex.printStackTrace();
284         }
285     }
286 
getLogger()287     private static Logger getLogger() {
288         return Logger.getLogger(OatParser.class.getSimpleName());
289     }
290 }
291