Home | History | Annotate | Download | only in dexlist
      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