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  */
descriptorToDot(const char * str)63 static std::unique_ptr<char[]> descriptorToDot(const char* str) {
64   size_t len = strlen(str);
65   if (str[0] == 'L') {
66     len -= 2;  // Two fewer chars to copy (trims L and ;).
67     str++;     // Start past 'L'.
68   }
69   std::unique_ptr<char[]> newStr(new char[len + 1]);
70   for (size_t i = 0; i < len; i++) {
71     newStr[i] = (str[i] == '/') ? '.' : str[i];
72   }
73   newStr[len] = '\0';
74   return newStr;
75 }
76 
77 /*
78  * Positions 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  */
positionsCb(void * context,const DexFile::PositionInfo & entry)82 static bool positionsCb(void* context, const DexFile::PositionInfo& entry) {
83   int* pFirstLine = reinterpret_cast<int *>(context);
84   if (*pFirstLine == -1) {
85     *pFirstLine = entry.line_;
86   }
87   return 0;
88 }
89 
90 /*
91  * Dumps a method.
92  */
dumpMethod(const DexFile * pDexFile,const char * fileName,u4 idx,u4 flags ATTRIBUTE_UNUSED,const DexFile::CodeItem * pCode,u4 codeOffset)93 static void dumpMethod(const DexFile* pDexFile,
94                        const char* fileName, u4 idx, u4 flags ATTRIBUTE_UNUSED,
95                        const DexFile::CodeItem* pCode, u4 codeOffset) {
96   // Abstract and native methods don't get listed.
97   if (pCode == nullptr || codeOffset == 0) {
98     return;
99   }
100 
101   // Method information.
102   const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
103   const char* methodName = pDexFile->StringDataByIdx(pMethodId.name_idx_);
104   const char* classDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
105   std::unique_ptr<char[]> className(descriptorToDot(classDescriptor));
106   const u4 insnsOff = codeOffset + 0x10;
107 
108   // Don't list methods that do not match a particular query.
109   if (gOptions.methodToFind != nullptr &&
110       (strcmp(gOptions.classToFind, className.get()) != 0 ||
111        strcmp(gOptions.methodToFind, methodName) != 0)) {
112     return;
113   }
114 
115   // If the filename is empty, then set it to something printable.
116   if (fileName == nullptr || fileName[0] == 0) {
117     fileName = "(none)";
118   }
119 
120   // Find the first line.
121   int firstLine = -1;
122   pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine);
123 
124   // Method signature.
125   const Signature signature = pDexFile->GetMethodSignature(pMethodId);
126   char* typeDesc = strdup(signature.ToString().c_str());
127 
128   // Dump actual method information.
129   fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n",
130           insnsOff, pCode->insns_size_in_code_units_ * 2,
131           className.get(), methodName, typeDesc, fileName, firstLine);
132 
133   free(typeDesc);
134 }
135 
136 /*
137  * Runs through all direct and virtual methods in the class.
138  */
dumpClass(const DexFile * pDexFile,u4 idx)139 void dumpClass(const DexFile* pDexFile, u4 idx) {
140   const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
141 
142   const char* fileName;
143   if (!pClassDef.source_file_idx_.IsValid()) {
144     fileName = nullptr;
145   } else {
146     fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_);
147   }
148 
149   const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
150   if (pEncodedData != nullptr) {
151     ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
152     // Skip the fields.
153     for (; pClassData.HasNextStaticField(); pClassData.Next()) {}
154     for (; pClassData.HasNextInstanceField(); pClassData.Next()) {}
155     // Direct methods.
156     for (; pClassData.HasNextDirectMethod(); pClassData.Next()) {
157       dumpMethod(pDexFile, fileName,
158                  pClassData.GetMemberIndex(),
159                  pClassData.GetRawMemberAccessFlags(),
160                  pClassData.GetMethodCodeItem(),
161                  pClassData.GetMethodCodeItemOffset());
162     }
163     // Virtual methods.
164     for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) {
165       dumpMethod(pDexFile, fileName,
166                  pClassData.GetMemberIndex(),
167                  pClassData.GetRawMemberAccessFlags(),
168                  pClassData.GetMethodCodeItem(),
169                  pClassData.GetMethodCodeItemOffset());
170     }
171   }
172 }
173 
174 /*
175  * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
176  */
processFile(const char * fileName)177 static int processFile(const char* fileName) {
178   // If the file is not a .dex file, the function tries .zip/.jar/.apk files,
179   // all of which are Zip archives with "classes.dex" inside.
180   static constexpr bool kVerifyChecksum = true;
181   std::string error_msg;
182   std::vector<std::unique_ptr<const DexFile>> dex_files;
183   if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
184     fputs(error_msg.c_str(), stderr);
185     fputc('\n', stderr);
186     return -1;
187   }
188 
189   // Success. Iterate over all dex files found in given file.
190   fprintf(gOutFile, "#%s\n", fileName);
191   for (size_t i = 0; i < dex_files.size(); i++) {
192     // Iterate over all classes in one dex file.
193     const DexFile* pDexFile = dex_files[i].get();
194     const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
195     for (u4 idx = 0; idx < classDefsSize; idx++) {
196       dumpClass(pDexFile, idx);
197     }
198   }
199   return 0;
200 }
201 
202 /*
203  * Shows usage.
204  */
usage(void)205 static void usage(void) {
206   fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
207   fprintf(stderr, "%s: [-m p.c.m] [-o outfile] dexfile...\n", gProgName);
208   fprintf(stderr, "\n");
209 }
210 
211 /*
212  * Main driver of the dexlist utility.
213  */
dexlistDriver(int argc,char ** argv)214 int dexlistDriver(int argc, char** argv) {
215   // Art specific set up.
216   InitLogging(argv, Runtime::Aborter);
217   MemMap::Init();
218 
219   // Reset options.
220   bool wantUsage = false;
221   memset(&gOptions, 0, sizeof(gOptions));
222 
223   // Parse all arguments.
224   while (1) {
225     const int ic = getopt(argc, argv, "o:m:");
226     if (ic < 0) {
227       break;  // done
228     }
229     switch (ic) {
230       case 'o':  // output file
231         gOptions.outputFileName = optarg;
232         break;
233       case 'm':
234         // If -m p.c.m is given, then find all instances of the
235         // fully-qualified method name. This isn't really what
236         // dexlist is for, but it's easy to do it here.
237         {
238           gOptions.argCopy = strdup(optarg);
239           char* meth = strrchr(gOptions.argCopy, '.');
240           if (meth == nullptr) {
241             fprintf(stderr, "Expected: package.Class.method\n");
242             wantUsage = true;
243           } else {
244             *meth = '\0';
245             gOptions.classToFind = gOptions.argCopy;
246             gOptions.methodToFind = meth + 1;
247           }
248         }
249         break;
250       default:
251         wantUsage = true;
252         break;
253     }  // switch
254   }  // while
255 
256   // Detect early problems.
257   if (optind == argc) {
258     fprintf(stderr, "%s: no file specified\n", gProgName);
259     wantUsage = true;
260   }
261   if (wantUsage) {
262     usage();
263     free(gOptions.argCopy);
264     return 2;
265   }
266 
267   // Open alternative output file.
268   if (gOptions.outputFileName) {
269     gOutFile = fopen(gOptions.outputFileName, "w");
270     if (!gOutFile) {
271       fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
272       free(gOptions.argCopy);
273       return 1;
274     }
275   }
276 
277   // Process all files supplied on command line. If one of them fails we
278   // continue on, only returning a failure at the end.
279   int result = 0;
280   while (optind < argc) {
281     result |= processFile(argv[optind++]);
282   }  // while
283 
284   free(gOptions.argCopy);
285   return result != 0;
286 }
287 
288 }  // namespace art
289 
main(int argc,char ** argv)290 int main(int argc, char** argv) {
291   return art::dexlistDriver(argc, argv);
292 }
293 
294