Home | History | Annotate | Download | only in dexlist
      1 /*
      2  * Copyright (C) 2008 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 /*
     18  * List all methods in all concrete classes in one or more DEX files.
     19  */
     20 
     21 #include "libdex/DexFile.h"
     22 
     23 #include "libdex/CmdUtils.h"
     24 #include "libdex/DexClass.h"
     25 #include "libdex/DexDebugInfo.h"
     26 #include "libdex/DexProto.h"
     27 #include "libdex/SysUtil.h"
     28 
     29 #include <stdlib.h>
     30 #include <stdio.h>
     31 #include <fcntl.h>
     32 #include <stddef.h>
     33 #include <string.h>
     34 #include <unistd.h>
     35 #include <getopt.h>
     36 #include <errno.h>
     37 #include <assert.h>
     38 
     39 static const char* gProgName = "dexlist";
     40 
     41 /* command-line args */
     42 static struct {
     43     char*       argCopy;
     44     const char* classToFind;
     45     const char* methodToFind;
     46 } gParms;
     47 
     48 
     49 /*
     50  * Return a newly-allocated string for the "dot version" of the class
     51  * name for the given type descriptor. That is, The initial "L" and
     52  * final ";" (if any) have been removed and all occurrences of '/'
     53  * have been changed to '.'.
     54  */
     55 static char* descriptorToDot(const char* str)
     56 {
     57     size_t at = strlen(str);
     58     char* newStr;
     59 
     60     if (str[0] == 'L') {
     61         assert(str[at - 1] == ';');
     62         at -= 2; /* Two fewer chars to copy. */
     63         str++; /* Skip the 'L'. */
     64     }
     65 
     66     newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
     67     newStr[at] = '\0';
     68 
     69     while (at > 0) {
     70         at--;
     71         newStr[at] = (str[at] == '/') ? '.' : str[at];
     72     }
     73 
     74     return newStr;
     75 }
     76 
     77 /*
     78  * Position table callback; we just want to catch the number of the
     79  * first line in the method, which *should* correspond to the first
     80  * entry from the table.  (Could also use "min" here.)
     81  */
     82 static int positionsCallback(void* cnxt, u4 address, u4 lineNum)
     83 {
     84     int* pFirstLine = (int*) cnxt;
     85     if (*pFirstLine == -1)
     86         *pFirstLine = lineNum;
     87     return 0;
     88 }
     89 
     90 
     91 /*
     92  * Dump a method.
     93  */
     94 void dumpMethod(DexFile* pDexFile, const char* fileName,
     95     const DexMethod* pDexMethod, int i)
     96 {
     97     const DexMethodId* pMethodId;
     98     const DexCode* pCode;
     99     const char* classDescriptor;
    100     const char* methodName;
    101     int firstLine;
    102 
    103     /* abstract and native methods don't get listed */
    104     if (pDexMethod->codeOff == 0)
    105         return;
    106 
    107     pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
    108     methodName = dexStringById(pDexFile, pMethodId->nameIdx);
    109 
    110     classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
    111 
    112     pCode = dexGetCode(pDexFile, pDexMethod);
    113     assert(pCode != NULL);
    114 
    115     /*
    116      * If the filename is empty, then set it to something printable
    117      * so that it is easier to parse.
    118      *
    119      * TODO: A method may override its class's default source file by
    120      * specifying a different one in its debug info. This possibility
    121      * should be handled here.
    122      */
    123     if (fileName == NULL || fileName[0] == 0) {
    124         fileName = "(none)";
    125     }
    126 
    127     firstLine = -1;
    128     dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
    129         pDexMethod->accessFlags, positionsCallback, NULL, &firstLine);
    130 
    131     char* className = descriptorToDot(classDescriptor);
    132     char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
    133     u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns);
    134 
    135     if (gParms.methodToFind != NULL &&
    136         (strcmp(gParms.classToFind, className) != 0 ||
    137          strcmp(gParms.methodToFind, methodName) != 0))
    138     {
    139         goto skip;
    140     }
    141 
    142     printf("0x%08x %d %s %s %s %s %d\n",
    143         insnsOff, pCode->insnsSize * 2,
    144         className, methodName, desc,
    145         fileName, firstLine);
    146 
    147 skip:
    148     free(desc);
    149     free(className);
    150 }
    151 
    152 /*
    153  * Run through all direct and virtual methods in the class.
    154  */
    155 void dumpClass(DexFile* pDexFile, int idx)
    156 {
    157     const DexClassDef* pClassDef;
    158     DexClassData* pClassData;
    159     const u1* pEncodedData;
    160     const char* fileName;
    161     int i;
    162 
    163     pClassDef = dexGetClassDef(pDexFile, idx);
    164     pEncodedData = dexGetClassData(pDexFile, pClassDef);
    165     pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
    166 
    167     if (pClassData == NULL) {
    168         fprintf(stderr, "Trouble reading class data\n");
    169         return;
    170     }
    171 
    172     if (pClassDef->sourceFileIdx == 0xffffffff) {
    173         fileName = NULL;
    174     } else {
    175         fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
    176     }
    177 
    178     /*
    179      * TODO: Each class def points at a sourceFile, so maybe that
    180      * should be printed out. However, this needs to be coordinated
    181      * with the tools that parse this output.
    182      */
    183 
    184     for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
    185         dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i);
    186     }
    187 
    188     for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
    189         dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i);
    190     }
    191 
    192     free(pClassData);
    193 }
    194 
    195 /*
    196  * Process a file.
    197  *
    198  * Returns 0 on success.
    199  */
    200 int process(const char* fileName)
    201 {
    202     DexFile* pDexFile = NULL;
    203     MemMapping map;
    204     bool mapped = false;
    205     int result = -1;
    206     UnzipToFileResult utfr;
    207 
    208     utfr = dexOpenAndMap(fileName, NULL, &map, true);
    209     if (utfr != kUTFRSuccess) {
    210         if (utfr == kUTFRNoClassesDex) {
    211             /* no classes.dex in the APK; pretend we succeeded */
    212             result = 0;
    213             goto bail;
    214         }
    215         fprintf(stderr, "Unable to process '%s'\n", fileName);
    216         goto bail;
    217     }
    218     mapped = true;
    219 
    220     pDexFile = dexFileParse((u1*)map.addr, map.length, kDexParseDefault);
    221     if (pDexFile == NULL) {
    222         fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName);
    223         goto bail;
    224     }
    225 
    226     printf("#%s\n", fileName);
    227 
    228     int i;
    229     for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
    230         dumpClass(pDexFile, i);
    231     }
    232 
    233     result = 0;
    234 
    235 bail:
    236     if (mapped)
    237         sysReleaseShmem(&map);
    238     if (pDexFile != NULL)
    239         dexFileFree(pDexFile);
    240     return result;
    241 }
    242 
    243 
    244 /*
    245  * Show usage.
    246  */
    247 void usage(void)
    248 {
    249     fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
    250     fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName);
    251     fprintf(stderr, "\n");
    252 }
    253 
    254 /*
    255  * Parse args.
    256  */
    257 int main(int argc, char* const argv[])
    258 {
    259     int result = 0;
    260     int i;
    261 
    262     /*
    263      * Find all instances of the fully-qualified method name.  This isn't
    264      * really what dexlist is for, but it's easy to do it here.
    265      */
    266     if (argc > 3 && strcmp(argv[1], "--method") == 0) {
    267         gParms.argCopy = strdup(argv[2]);
    268         char* meth = strrchr(gParms.argCopy, '.');
    269         if (meth == NULL) {
    270             fprintf(stderr, "Expected package.Class.method\n");
    271             free(gParms.argCopy);
    272             return 2;
    273         }
    274         *meth = '\0';
    275         gParms.classToFind = gParms.argCopy;
    276         gParms.methodToFind = meth+1;
    277         argv += 2;
    278         argc -= 2;
    279     }
    280 
    281     if (argc < 2) {
    282         fprintf(stderr, "%s: no file specified\n", gProgName);
    283         usage();
    284         return 2;
    285     }
    286 
    287     /*
    288      * Run through the list of files.  If one of them fails we contine on,
    289      * only returning a failure at the end.
    290      */
    291     for (i = 1; i < argc; i++)
    292         result |= process(argv[i]);
    293 
    294     free(gParms.argCopy);
    295     return result;
    296 }
    297