• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
descriptorToDot(const char * str)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  */
positionsCallback(void * cnxt,u4 address,u4 lineNum)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  */
dumpMethod(DexFile * pDexFile,const char * fileName,const DexMethod * pDexMethod,int i)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  */
dumpClass(DexFile * pDexFile,int idx)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  */
process(const char * fileName)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  */
usage(void)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  */
main(int argc,char * const argv[])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