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