1 /* 2 * Copyright (C) 2015 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 * Implementation file of the dexlist utility. 17 * 18 * This is a re-implementation of the original dexlist utility that was 19 * based on Dalvik functions in libdex into a new dexlist that is now 20 * based on Art functions in libart instead. The output is identical to 21 * the original for correct DEX files. Error messages may differ, however. 22 * 23 * List all methods in all concrete classes in one or more DEX files. 24 */ 25 26 #include <stdlib.h> 27 #include <stdio.h> 28 29 #include "dex_file-inl.h" 30 #include "mem_map.h" 31 #include "runtime.h" 32 33 namespace art { 34 35 static const char* gProgName = "dexlist"; 36 37 /* Command-line options. */ 38 static struct { 39 char* argCopy; 40 const char* classToFind; 41 const char* methodToFind; 42 const char* outputFileName; 43 } gOptions; 44 45 /* 46 * Output file. Defaults to stdout. 47 */ 48 static FILE* gOutFile = stdout; 49 50 /* 51 * Data types that match the definitions in the VM specification. 52 */ 53 typedef uint8_t u1; 54 typedef uint32_t u4; 55 typedef uint64_t u8; 56 57 /* 58 * Returns a newly-allocated string for the "dot version" of the class 59 * name for the given type descriptor. That is, The initial "L" and 60 * final ";" (if any) have been removed and all occurrences of '/' 61 * have been changed to '.'. 62 */ 63 static char* descriptorToDot(const char* str) { 64 size_t at = strlen(str); 65 if (str[0] == 'L') { 66 at -= 2; // Two fewer chars to copy. 67 str++; 68 } 69 char* newStr = reinterpret_cast<char*>(malloc(at + 1)); 70 newStr[at] = '\0'; 71 while (at > 0) { 72 at--; 73 newStr[at] = (str[at] == '/') ? '.' : str[at]; 74 } 75 return newStr; 76 } 77 78 /* 79 * Positions table callback; we just want to catch the number of the 80 * first line in the method, which *should* correspond to the first 81 * entry from the table. (Could also use "min" here.) 82 */ 83 static bool positionsCb(void* context, const DexFile::PositionInfo& entry) { 84 int* pFirstLine = reinterpret_cast<int *>(context); 85 if (*pFirstLine == -1) { 86 *pFirstLine = entry.line_; 87 } 88 return 0; 89 } 90 91 /* 92 * Dumps a method. 93 */ 94 static void dumpMethod(const DexFile* pDexFile, 95 const char* fileName, u4 idx, u4 flags ATTRIBUTE_UNUSED, 96 const DexFile::CodeItem* pCode, u4 codeOffset) { 97 // Abstract and native methods don't get listed. 98 if (pCode == nullptr || codeOffset == 0) { 99 return; 100 } 101 102 // Method information. 103 const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); 104 const char* methodName = pDexFile->StringDataByIdx(pMethodId.name_idx_); 105 const char* classDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); 106 char* className = descriptorToDot(classDescriptor); 107 const u4 insnsOff = codeOffset + 0x10; 108 109 // Don't list methods that do not match a particular query. 110 if (gOptions.methodToFind != nullptr && 111 (strcmp(gOptions.classToFind, className) != 0 || 112 strcmp(gOptions.methodToFind, methodName) != 0)) { 113 free(className); 114 return; 115 } 116 117 // If the filename is empty, then set it to something printable. 118 if (fileName == nullptr || fileName[0] == 0) { 119 fileName = "(none)"; 120 } 121 122 // Find the first line. 123 int firstLine = -1; 124 pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine); 125 126 // Method signature. 127 const Signature signature = pDexFile->GetMethodSignature(pMethodId); 128 char* typeDesc = strdup(signature.ToString().c_str()); 129 130 // Dump actual method information. 131 fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n", 132 insnsOff, pCode->insns_size_in_code_units_ * 2, 133 className, methodName, typeDesc, fileName, firstLine); 134 135 free(typeDesc); 136 free(className); 137 } 138 139 /* 140 * Runs through all direct and virtual methods in the class. 141 */ 142 void dumpClass(const DexFile* pDexFile, u4 idx) { 143 const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); 144 145 const char* fileName; 146 if (pClassDef.source_file_idx_ == DexFile::kDexNoIndex) { 147 fileName = nullptr; 148 } else { 149 fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_); 150 } 151 152 const u1* pEncodedData = pDexFile->GetClassData(pClassDef); 153 if (pEncodedData != nullptr) { 154 ClassDataItemIterator pClassData(*pDexFile, pEncodedData); 155 // Skip the fields. 156 for (; pClassData.HasNextStaticField(); pClassData.Next()) {} 157 for (; pClassData.HasNextInstanceField(); pClassData.Next()) {} 158 // Direct methods. 159 for (; pClassData.HasNextDirectMethod(); pClassData.Next()) { 160 dumpMethod(pDexFile, fileName, 161 pClassData.GetMemberIndex(), 162 pClassData.GetRawMemberAccessFlags(), 163 pClassData.GetMethodCodeItem(), 164 pClassData.GetMethodCodeItemOffset()); 165 } 166 // Virtual methods. 167 for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) { 168 dumpMethod(pDexFile, fileName, 169 pClassData.GetMemberIndex(), 170 pClassData.GetRawMemberAccessFlags(), 171 pClassData.GetMethodCodeItem(), 172 pClassData.GetMethodCodeItemOffset()); 173 } 174 } 175 } 176 177 /* 178 * Processes a single file (either direct .dex or indirect .zip/.jar/.apk). 179 */ 180 static int processFile(const char* fileName) { 181 // If the file is not a .dex file, the function tries .zip/.jar/.apk files, 182 // all of which are Zip archives with "classes.dex" inside. 183 std::string error_msg; 184 std::vector<std::unique_ptr<const DexFile>> dex_files; 185 if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { 186 fputs(error_msg.c_str(), stderr); 187 fputc('\n', stderr); 188 return -1; 189 } 190 191 // Success. Iterate over all dex files found in given file. 192 fprintf(gOutFile, "#%s\n", fileName); 193 for (size_t i = 0; i < dex_files.size(); i++) { 194 // Iterate over all classes in one dex file. 195 const DexFile* pDexFile = dex_files[i].get(); 196 const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_; 197 for (u4 idx = 0; idx < classDefsSize; idx++) { 198 dumpClass(pDexFile, idx); 199 } 200 } 201 return 0; 202 } 203 204 /* 205 * Shows usage. 206 */ 207 static void usage(void) { 208 fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); 209 fprintf(stderr, "%s: [-m p.c.m] [-o outfile] dexfile...\n", gProgName); 210 fprintf(stderr, "\n"); 211 } 212 213 /* 214 * Main driver of the dexlist utility. 215 */ 216 int dexlistDriver(int argc, char** argv) { 217 // Art specific set up. 218 InitLogging(argv); 219 MemMap::Init(); 220 221 // Reset options. 222 bool wantUsage = false; 223 memset(&gOptions, 0, sizeof(gOptions)); 224 225 // Parse all arguments. 226 while (1) { 227 const int ic = getopt(argc, argv, "o:m:"); 228 if (ic < 0) { 229 break; // done 230 } 231 switch (ic) { 232 case 'o': // output file 233 gOptions.outputFileName = optarg; 234 break; 235 case 'm': 236 // If -m p.c.m is given, then find all instances of the 237 // fully-qualified method name. This isn't really what 238 // dexlist is for, but it's easy to do it here. 239 { 240 gOptions.argCopy = strdup(optarg); 241 char* meth = strrchr(gOptions.argCopy, '.'); 242 if (meth == nullptr) { 243 fprintf(stderr, "Expected: package.Class.method\n"); 244 wantUsage = true; 245 } else { 246 *meth = '\0'; 247 gOptions.classToFind = gOptions.argCopy; 248 gOptions.methodToFind = meth + 1; 249 } 250 } 251 break; 252 default: 253 wantUsage = true; 254 break; 255 } // switch 256 } // while 257 258 // Detect early problems. 259 if (optind == argc) { 260 fprintf(stderr, "%s: no file specified\n", gProgName); 261 wantUsage = true; 262 } 263 if (wantUsage) { 264 usage(); 265 free(gOptions.argCopy); 266 return 2; 267 } 268 269 // Open alternative output file. 270 if (gOptions.outputFileName) { 271 gOutFile = fopen(gOptions.outputFileName, "w"); 272 if (!gOutFile) { 273 fprintf(stderr, "Can't open %s\n", gOptions.outputFileName); 274 free(gOptions.argCopy); 275 return 1; 276 } 277 } 278 279 // Process all files supplied on command line. If one of them fails we 280 // continue on, only returning a failure at the end. 281 int result = 0; 282 while (optind < argc) { 283 result |= processFile(argv[optind++]); 284 } // while 285 286 free(gOptions.argCopy); 287 return result != 0; 288 } 289 290 } // namespace art 291 292 int main(int argc, char** argv) { 293 return art::dexlistDriver(argc, argv); 294 } 295 296