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 import com.google.protobuf.TextFormat; 22 23 import java.io.File; 24 import java.io.FileOutputStream; 25 import java.io.IOException; 26 import java.nio.charset.Charset; 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.List; 30 import java.util.logging.Level; 31 import java.util.logging.Logger; 32 33 public class SoParser extends FileParser { 34 private int mBits; 35 private String mArch; 36 private List<String> mDependencies; 37 private List<String> mDynamicLoadingDependencies; 38 private ReadElf mElf; 39 private String mPackageName; 40 private ApiPackage.Builder mExternalApiPackageBuilder; 41 private HashMap<String, ApiClass.Builder> mExternalApiClassBuilderMap; 42 private ApiPackage.Builder mInternalApiPackageBuilder; 43 private AppInfo.Builder mAppInfoBuilder; 44 private boolean mParseInternalApi; 45 SoParser(File file)46 public SoParser(File file) { 47 super(file); 48 mBits = 0; 49 mArch = null; 50 mDependencies = null; 51 mDynamicLoadingDependencies = null; 52 mAppInfoBuilder = null; 53 // default is the file name with out extenion 54 mPackageName = getFileName().split("\\.")[0]; 55 // default off to avoid a large output 56 mParseInternalApi = false; 57 } 58 59 @Override getType()60 public Entry.EntryType getType() { 61 return Entry.EntryType.SO; 62 } 63 64 @Override getCodeId()65 public String getCodeId() { 66 return getFileContentId(); 67 } 68 69 @Override getDependencies()70 public List<String> getDependencies() { 71 if (mDependencies == null) { 72 parse(); 73 } 74 return mDependencies; 75 } 76 77 @Override getDynamicLoadingDependencies()78 public List<String> getDynamicLoadingDependencies() { 79 if (mDynamicLoadingDependencies == null) { 80 parse(); 81 } 82 return mDynamicLoadingDependencies; 83 } 84 85 @Override getAbiBits()86 public int getAbiBits() { 87 if (mBits == 0) { 88 parse(); 89 } 90 return mBits; 91 } 92 93 @Override getAbiArchitecture()94 public String getAbiArchitecture() { 95 if (mArch == null) { 96 parse(); 97 } 98 return mArch; 99 } 100 setPackageName(String name)101 public void setPackageName(String name) { 102 String[] subStr = name.split(File.separator); 103 mPackageName = subStr[subStr.length - 1]; 104 } 105 setParseInternalApi(boolean parseInternalApi)106 public void setParseInternalApi(boolean parseInternalApi) { 107 mParseInternalApi = parseInternalApi; 108 } 109 getAppInfo()110 public AppInfo getAppInfo() { 111 if (mAppInfoBuilder == null) { 112 mAppInfoBuilder = AppInfo.newBuilder(); 113 mAppInfoBuilder.setPackageName(mPackageName); 114 mAppInfoBuilder.addInternalApiPackages(getInternalApiPackage()); 115 mAppInfoBuilder.addExternalApiPackages(getExternalApiPackage()); 116 } 117 return mAppInfoBuilder.build(); 118 } 119 getExternalApiPackage()120 public ApiPackage getExternalApiPackage() { 121 if (mExternalApiPackageBuilder == null) { 122 parse(); 123 } 124 return mExternalApiPackageBuilder.build(); 125 } 126 getInternalApiPackage()127 public ApiPackage getInternalApiPackage() { 128 if (mInternalApiPackageBuilder == null) { 129 parse(); 130 } 131 return mInternalApiPackageBuilder.build(); 132 } 133 parse()134 private void parse() { 135 mExternalApiPackageBuilder = ApiPackage.newBuilder(); 136 mExternalApiClassBuilderMap = new HashMap<String, ApiClass.Builder>(); 137 mInternalApiPackageBuilder = ApiPackage.newBuilder(); 138 try { 139 ReadElf mElf = ReadElf.read(getFile()); 140 mBits = mElf.getBits(); 141 mArch = mElf.getArchitecture(); 142 mDependencies = mElf.getDynamicDependencies(); 143 // Check Dynamic Loading dependencies 144 mDynamicLoadingDependencies = getDynamicLoadingDependencies(mElf); 145 parseApi(mElf.getDynSymArr()); 146 } catch (Exception ex) { 147 mDependencies = super.getDependencies(); 148 mDynamicLoadingDependencies = super.getDynamicLoadingDependencies(); 149 mBits = -1; 150 mArch = "unknown"; 151 getLogger() 152 .log( 153 Level.SEVERE, 154 String.format( 155 "SoParser fails to parse %s. \n%s", 156 getFileName(), ex.getMessage())); 157 ex.printStackTrace(); 158 } 159 } 160 parseApi(ReadElf.Symbol[] symArr)161 private void parseApi(ReadElf.Symbol[] symArr) { 162 ApiClass.Builder mInternalApiClassBuilder = ApiClass.newBuilder(); 163 mInternalApiClassBuilder.setName(mPackageName); 164 165 for (ReadElf.Symbol symbol : symArr) { 166 if (symbol.isExtern()) { 167 // Internal methods & fields 168 if (mParseInternalApi) { 169 // Skips reference symbols 170 if (isInternalReferenceSymbol(symbol)) { 171 continue; 172 } 173 if (symbol.type == ReadElf.Symbol.STT_OBJECT) { 174 ApiField.Builder fieldBuilder = ApiField.newBuilder(); 175 fieldBuilder.setName(symbol.name); 176 mInternalApiClassBuilder.addFields(fieldBuilder.build()); 177 } else { 178 ApiMethod.Builder methodBuilder = ApiMethod.newBuilder(); 179 methodBuilder.setName(symbol.name); 180 mInternalApiClassBuilder.addMethods(methodBuilder.build()); 181 } 182 } 183 } else if (symbol.isGlobalUnd()) { 184 // External dependency 185 String className = symbol.getExternalLibFileName(); 186 ApiClass.Builder apiClassBuilder = 187 ClassUtils.getApiClassBuilder(mExternalApiClassBuilderMap, className); 188 if (symbol.type == ReadElf.Symbol.STT_OBJECT) { 189 ApiField.Builder fieldBuilder = ApiField.newBuilder(); 190 fieldBuilder.setName(symbol.name); 191 apiClassBuilder.addFields(fieldBuilder.build()); 192 } else { 193 ApiMethod.Builder methodBuilder = ApiMethod.newBuilder(); 194 methodBuilder.setName(symbol.name); 195 apiClassBuilder.addMethods(methodBuilder.build()); 196 } 197 } 198 } 199 if (mParseInternalApi) { 200 mInternalApiPackageBuilder.addClasses(mInternalApiClassBuilder.build()); 201 } 202 ClassUtils.addAllApiClasses(mExternalApiClassBuilderMap, mExternalApiPackageBuilder); 203 } 204 getApiClassBuilder( HashMap<String, ApiClass.Builder> apiClassBuilderMap, String name)205 private ApiClass.Builder getApiClassBuilder( 206 HashMap<String, ApiClass.Builder> apiClassBuilderMap, String name) { 207 ApiClass.Builder builder = apiClassBuilderMap.get(name); 208 if (builder == null) { 209 builder = ApiClass.newBuilder().setName(ClassUtils.getCanonicalName(name)); 210 apiClassBuilderMap.put(name, builder); 211 } 212 return builder; 213 } 214 215 // internal reference symboles 216 private static final HashMap<String, String> sInternalReferenceSymboleMap; 217 218 static { 219 sInternalReferenceSymboleMap = new HashMap<String, String>(); 220 sInternalReferenceSymboleMap.put("__bss_start", "bss"); 221 sInternalReferenceSymboleMap.put("_end", "initialized data"); 222 sInternalReferenceSymboleMap.put("_edata", "uninitialized data"); 223 } 224 isInternalReferenceSymbol(ReadElf.Symbol sym)225 private static boolean isInternalReferenceSymbol(ReadElf.Symbol sym) { 226 String value = sInternalReferenceSymboleMap.get(sym.name); 227 if (value == null) { 228 return false; 229 } else { 230 return true; 231 } 232 } 233 getDynamicLoadingDependencies(ReadElf elf)234 private List<String> getDynamicLoadingDependencies(ReadElf elf) throws IOException { 235 List<String> depList = new ArrayList<>(); 236 // check if it does refer to dlopen 237 if (elf.getDynamicSymbol("dlopen") != null) { 238 List<String> roStrings = elf.getRoStrings(); 239 for (String str : roStrings) { 240 // skip ".so" or less 241 if (str.length() < 4) { 242 continue; 243 } 244 245 if (str.endsWith(".so")) { 246 // skip itself 247 if (str.contains(getFileName())) { 248 continue; 249 } 250 if (str.contains(" ")) { 251 continue; 252 } 253 if (str.contains("?")) { 254 continue; 255 } 256 if (str.contains("%")) { 257 System.err.println("ToDo getDynamicLoadingDependencies: " + str); 258 continue; 259 } 260 if (str.startsWith("_")) { 261 System.err.println("ToDo getDynamicLoadingDependencies: " + str); 262 continue; 263 } 264 depList.add(str); 265 } 266 } 267 } 268 269 // specific for frameworks/native/opengl/libs/EGL/Loader.cpp load_system_driver() 270 if ("libEGL.so".equals(getFileName())) { 271 depList.add("libEGL*.so"); 272 depList.add("libGLESv1_CM*.so"); 273 depList.add("GLESv2*.so"); 274 } 275 276 return depList; 277 } 278 279 private static final String USAGE_MESSAGE = 280 "Usage: java -jar releaseparser.jar " 281 + ApkParser.class.getCanonicalName() 282 + " [-options <parameter>]...\n" 283 + " to prase SO file meta data\n" 284 + "Options:\n" 285 + "\t-i PATH\t The file path of the file to be parsed.\n" 286 + "\t-pi \t Parses internal methods and fields too. Output will be large when parsing multiple files in a release.\n" 287 + "\t-of PATH\t The file path of the output file instead of printing to System.out.\n"; 288 main(String[] args)289 public static void main(String[] args) { 290 try { 291 ArgumentParser argParser = new ArgumentParser(args); 292 String fileName = argParser.getParameterElement("i", 0); 293 String outputFileName = argParser.getParameterElement("of", 0); 294 boolean parseInternalApi = argParser.containsOption("pi"); 295 296 File aFile = new File(fileName); 297 SoParser aParser = new SoParser(aFile); 298 aParser.setPackageName(fileName); 299 aParser.setParseInternalApi(parseInternalApi); 300 301 if (outputFileName != null) { 302 FileOutputStream txtOutput = new FileOutputStream(outputFileName); 303 txtOutput.write( 304 TextFormat.printToString(aParser.getAppInfo()) 305 .getBytes(Charset.forName("UTF-8"))); 306 txtOutput.flush(); 307 txtOutput.close(); 308 } else { 309 System.out.println(TextFormat.printToString(aParser.getAppInfo())); 310 } 311 } catch (Exception ex) { 312 System.out.println(USAGE_MESSAGE); 313 ex.printStackTrace(); 314 } 315 } 316 getLogger()317 private static Logger getLogger() { 318 return Logger.getLogger(SoParser.class.getSimpleName()); 319 } 320 } 321