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.cts.releaseparser.ReleaseProto.*; 20 21 import java.io.File; 22 import java.io.RandomAccessFile; 23 import java.util.Arrays; 24 import java.util.logging.Logger; 25 26 // art/runtime/vdex_file.h & vdex_file.cc 27 public class VdexParser extends FileParser { 28 // The magic values for the VDEX identification. 29 private static final byte[] VDEX_MAGIC = {(byte) 'v', (byte) 'd', (byte) 'e', (byte) 'x'}; 30 private static final int HEADER_SIZE = 64; 31 private VdexInfo.Builder mVdexInfoBuilder; 32 VdexParser(File file)33 public VdexParser(File file) { 34 super(file); 35 } 36 37 @Override getType()38 public Entry.EntryType getType() { 39 return Entry.EntryType.VDEX; 40 } 41 42 @Override setAdditionalInfo()43 public void setAdditionalInfo() { 44 getFileEntryBuilder().setVdexInfo(getVdexInfo()); 45 } 46 47 @Override getCodeId()48 public String getCodeId() { 49 if (mVdexInfoBuilder == null) { 50 parse(); 51 } 52 return mCodeId; 53 } 54 getVdexInfo()55 public VdexInfo getVdexInfo() { 56 if (mVdexInfoBuilder == null) { 57 parse(); 58 } 59 return mVdexInfoBuilder.build(); 60 } 61 parse()62 private void parse() { 63 byte[] buffer = new byte[HEADER_SIZE]; 64 mVdexInfoBuilder = VdexInfo.newBuilder(); 65 66 try { 67 RandomAccessFile raFile = new RandomAccessFile(getFile(), "r"); 68 raFile.seek(0); 69 raFile.readFully(buffer, 0, HEADER_SIZE); 70 raFile.close(); 71 72 // ToDo: this is specific for 019 VerifierDepsVersion. Need to handle changes for older 73 // versions 74 if (buffer[0] != VDEX_MAGIC[0] 75 || buffer[1] != VDEX_MAGIC[1] 76 || buffer[2] != VDEX_MAGIC[2] 77 || buffer[3] != VDEX_MAGIC[3]) { 78 String content = new String(buffer); 79 System.err.println("Invalid VDEX file:" + getFileName() + " " + content); 80 throw new IllegalArgumentException("Invalid VDEX MAGIC"); 81 } 82 int offset = 4; 83 String version = new String(Arrays.copyOfRange(buffer, offset, offset + 4)); 84 mVdexInfoBuilder.setVerifierDepsVersion(version); 85 offset += 4; 86 String dex_section_version = new String(Arrays.copyOfRange(buffer, offset, offset + 4)); 87 mVdexInfoBuilder.setDexSectionVersion(dex_section_version); 88 offset += 4; 89 int numberOfDexFiles = getIntLittleEndian(buffer, offset); 90 mVdexInfoBuilder.setNumberOfDexFiles(numberOfDexFiles); 91 offset += 4; 92 mVdexInfoBuilder.setVerifierDepsSize(getIntLittleEndian(buffer, offset)); 93 offset += 4; 94 95 // Code Id format: [0xchecksum1],... 96 StringBuilder codeIdSB = new StringBuilder(); 97 for (int i = 0; i < numberOfDexFiles; i++) { 98 int checksums = getIntLittleEndian(buffer, offset); 99 offset += 4; 100 mVdexInfoBuilder.addChecksums(checksums); 101 codeIdSB.append(String.format(CODE_ID_FORMAT, checksums)); 102 } 103 mCodeId = codeIdSB.toString(); 104 105 for (int i = 0; i < numberOfDexFiles; i++) { 106 DexSectionHeader.Builder dshBuilder = DexSectionHeader.newBuilder(); 107 dshBuilder.setDexSize(getIntLittleEndian(buffer, offset)); 108 offset += 4; 109 dshBuilder.setDexSharedDataSize(getIntLittleEndian(buffer, offset)); 110 offset += 4; 111 dshBuilder.setQuickeningInfoSize(getIntLittleEndian(buffer, offset)); 112 offset += 4; 113 mVdexInfoBuilder.addDexSectionHeaders(dshBuilder.build()); 114 offset += 4; 115 } 116 117 for (int i = 0; i < numberOfDexFiles; i++) { 118 int quicken_table_off = getIntLittleEndian(buffer, offset); 119 offset += 4; 120 // Todo processing Dex 121 } 122 123 } catch (Exception ex) { 124 System.err.println("Invalid VDEX file:" + getFileName()); 125 mVdexInfoBuilder.setValid(false); 126 } 127 } 128 129 private static final String USAGE_MESSAGE = 130 "Usage: java -jar releaseparser.jar " 131 + VdexParser.class.getCanonicalName() 132 + " [-options <parameter>]...\n" 133 + " to parse VDEX file meta data\n" 134 + "Options:\n" 135 + "\t-i PATH\t The file path of the file to be parsed.\n" 136 + "\t-of PATH\t The file path of the output file instead of printing to System.out.\n"; 137 main(String[] args)138 public static void main(String[] args) { 139 try { 140 ArgumentParser argParser = new ArgumentParser(args); 141 String fileName = argParser.getParameterElement("i", 0); 142 String outputFileName = argParser.getParameterElement("of", 0); 143 144 File aFile = new File(fileName); 145 VdexParser aParser = new VdexParser(aFile); 146 Entry fileEntry = aParser.getFileEntryBuilder().build(); 147 writeTextFormatMessage(outputFileName, fileEntry); 148 } catch (Exception ex) { 149 System.out.println(USAGE_MESSAGE); 150 ex.printStackTrace(); 151 } 152 } 153 getLogger()154 private static Logger getLogger() { 155 return Logger.getLogger(VdexParser.class.getSimpleName()); 156 } 157 } 158