xref: /aosp_15_r20/art/dexlist/dexlist.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  *
16*795d594fSAndroid Build Coastguard Worker  * Implementation file of the dexlist utility.
17*795d594fSAndroid Build Coastguard Worker  *
18*795d594fSAndroid Build Coastguard Worker  * This is a re-implementation of the original dexlist utility that was
19*795d594fSAndroid Build Coastguard Worker  * based on Dalvik functions in libdex into a new dexlist that is now
20*795d594fSAndroid Build Coastguard Worker  * based on Art functions in libart instead. The output is identical to
21*795d594fSAndroid Build Coastguard Worker  * the original for correct DEX files. Error messages may differ, however.
22*795d594fSAndroid Build Coastguard Worker  *
23*795d594fSAndroid Build Coastguard Worker  * List all methods in all concrete classes in one or more DEX files.
24*795d594fSAndroid Build Coastguard Worker  */
25*795d594fSAndroid Build Coastguard Worker 
26*795d594fSAndroid Build Coastguard Worker #include <inttypes.h>
27*795d594fSAndroid Build Coastguard Worker #include <stdio.h>
28*795d594fSAndroid Build Coastguard Worker #include <stdlib.h>
29*795d594fSAndroid Build Coastguard Worker 
30*795d594fSAndroid Build Coastguard Worker #include <android-base/file.h>
31*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker #include "base/mem_map.h"
34*795d594fSAndroid Build Coastguard Worker #include "dex/class_accessor-inl.h"
35*795d594fSAndroid Build Coastguard Worker #include "dex/code_item_accessors-inl.h"
36*795d594fSAndroid Build Coastguard Worker #include "dex/dex_file-inl.h"
37*795d594fSAndroid Build Coastguard Worker #include "dex/dex_file_loader.h"
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker namespace art {
40*795d594fSAndroid Build Coastguard Worker 
41*795d594fSAndroid Build Coastguard Worker static const char* gProgName = "dexlist";
42*795d594fSAndroid Build Coastguard Worker 
43*795d594fSAndroid Build Coastguard Worker /* Command-line options. */
44*795d594fSAndroid Build Coastguard Worker static struct {
45*795d594fSAndroid Build Coastguard Worker   char* argCopy;
46*795d594fSAndroid Build Coastguard Worker   const char* classToFind;
47*795d594fSAndroid Build Coastguard Worker   const char* methodToFind;
48*795d594fSAndroid Build Coastguard Worker   const char* outputFileName;
49*795d594fSAndroid Build Coastguard Worker } gOptions;
50*795d594fSAndroid Build Coastguard Worker 
51*795d594fSAndroid Build Coastguard Worker /*
52*795d594fSAndroid Build Coastguard Worker  * Output file. Defaults to stdout.
53*795d594fSAndroid Build Coastguard Worker  */
54*795d594fSAndroid Build Coastguard Worker static FILE* gOutFile = stdout;
55*795d594fSAndroid Build Coastguard Worker 
56*795d594fSAndroid Build Coastguard Worker /*
57*795d594fSAndroid Build Coastguard Worker  * Data types that match the definitions in the VM specification.
58*795d594fSAndroid Build Coastguard Worker  */
59*795d594fSAndroid Build Coastguard Worker using u1 = uint8_t;
60*795d594fSAndroid Build Coastguard Worker using u4 = uint32_t;
61*795d594fSAndroid Build Coastguard Worker using u8 = uint64_t;
62*795d594fSAndroid Build Coastguard Worker 
63*795d594fSAndroid Build Coastguard Worker /*
64*795d594fSAndroid Build Coastguard Worker  * Returns a newly-allocated string for the "dot version" of the class
65*795d594fSAndroid Build Coastguard Worker  * name for the given type descriptor. That is, The initial "L" and
66*795d594fSAndroid Build Coastguard Worker  * final ";" (if any) have been removed and all occurrences of '/'
67*795d594fSAndroid Build Coastguard Worker  * have been changed to '.'.
68*795d594fSAndroid Build Coastguard Worker  */
descriptorToDot(const char * str)69*795d594fSAndroid Build Coastguard Worker static std::unique_ptr<char[]> descriptorToDot(const char* str) {
70*795d594fSAndroid Build Coastguard Worker   size_t len = strlen(str);
71*795d594fSAndroid Build Coastguard Worker   if (str[0] == 'L') {
72*795d594fSAndroid Build Coastguard Worker     len -= 2;  // Two fewer chars to copy (trims L and ;).
73*795d594fSAndroid Build Coastguard Worker     str++;     // Start past 'L'.
74*795d594fSAndroid Build Coastguard Worker   }
75*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<char[]> newStr(new char[len + 1]);
76*795d594fSAndroid Build Coastguard Worker   for (size_t i = 0; i < len; i++) {
77*795d594fSAndroid Build Coastguard Worker     newStr[i] = (str[i] == '/') ? '.' : str[i];
78*795d594fSAndroid Build Coastguard Worker   }
79*795d594fSAndroid Build Coastguard Worker   newStr[len] = '\0';
80*795d594fSAndroid Build Coastguard Worker   return newStr;
81*795d594fSAndroid Build Coastguard Worker }
82*795d594fSAndroid Build Coastguard Worker 
83*795d594fSAndroid Build Coastguard Worker /*
84*795d594fSAndroid Build Coastguard Worker  * Dumps a method.
85*795d594fSAndroid Build Coastguard Worker  */
dumpMethod(const DexFile * pDexFile,const char * fileName,u4 idx,u4 flags,const dex::CodeItem * pCode,u4 codeOffset)86*795d594fSAndroid Build Coastguard Worker static void dumpMethod(const DexFile* pDexFile,
87*795d594fSAndroid Build Coastguard Worker                        const char* fileName,
88*795d594fSAndroid Build Coastguard Worker                        u4 idx,
89*795d594fSAndroid Build Coastguard Worker                        [[maybe_unused]] u4 flags,
90*795d594fSAndroid Build Coastguard Worker                        const dex::CodeItem* pCode,
91*795d594fSAndroid Build Coastguard Worker                        u4 codeOffset) {
92*795d594fSAndroid Build Coastguard Worker   // Abstract and native methods don't get listed.
93*795d594fSAndroid Build Coastguard Worker   if (pCode == nullptr || codeOffset == 0) {
94*795d594fSAndroid Build Coastguard Worker     return;
95*795d594fSAndroid Build Coastguard Worker   }
96*795d594fSAndroid Build Coastguard Worker   CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx);
97*795d594fSAndroid Build Coastguard Worker 
98*795d594fSAndroid Build Coastguard Worker   // Method information.
99*795d594fSAndroid Build Coastguard Worker   const dex::MethodId& pMethodId = pDexFile->GetMethodId(idx);
100*795d594fSAndroid Build Coastguard Worker   const char* methodName = pDexFile->GetStringData(pMethodId.name_idx_);
101*795d594fSAndroid Build Coastguard Worker   const char* classDescriptor = pDexFile->GetTypeDescriptor(pMethodId.class_idx_);
102*795d594fSAndroid Build Coastguard Worker   std::unique_ptr<char[]> className(descriptorToDot(classDescriptor));
103*795d594fSAndroid Build Coastguard Worker   const u4 insnsOff = codeOffset + 0x10;
104*795d594fSAndroid Build Coastguard Worker 
105*795d594fSAndroid Build Coastguard Worker   // Don't list methods that do not match a particular query.
106*795d594fSAndroid Build Coastguard Worker   if (gOptions.methodToFind != nullptr &&
107*795d594fSAndroid Build Coastguard Worker       (strcmp(gOptions.classToFind, className.get()) != 0 ||
108*795d594fSAndroid Build Coastguard Worker        strcmp(gOptions.methodToFind, methodName) != 0)) {
109*795d594fSAndroid Build Coastguard Worker     return;
110*795d594fSAndroid Build Coastguard Worker   }
111*795d594fSAndroid Build Coastguard Worker 
112*795d594fSAndroid Build Coastguard Worker   // If the filename is empty, then set it to something printable.
113*795d594fSAndroid Build Coastguard Worker   if (fileName == nullptr || fileName[0] == 0) {
114*795d594fSAndroid Build Coastguard Worker     fileName = "(none)";
115*795d594fSAndroid Build Coastguard Worker   }
116*795d594fSAndroid Build Coastguard Worker 
117*795d594fSAndroid Build Coastguard Worker   // We just want to catch the number of the first line in the method, which *should* correspond to
118*795d594fSAndroid Build Coastguard Worker   // the first entry from the table.
119*795d594fSAndroid Build Coastguard Worker   int first_line = -1;
120*795d594fSAndroid Build Coastguard Worker   accessor.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
121*795d594fSAndroid Build Coastguard Worker     first_line = entry.line_;
122*795d594fSAndroid Build Coastguard Worker     return true;  // Early exit since we only want the first line.
123*795d594fSAndroid Build Coastguard Worker   });
124*795d594fSAndroid Build Coastguard Worker 
125*795d594fSAndroid Build Coastguard Worker   // Method signature.
126*795d594fSAndroid Build Coastguard Worker   const Signature signature = pDexFile->GetMethodSignature(pMethodId);
127*795d594fSAndroid Build Coastguard Worker   char* typeDesc = strdup(signature.ToString().c_str());
128*795d594fSAndroid Build Coastguard Worker 
129*795d594fSAndroid Build Coastguard Worker   // Dump actual method information.
130*795d594fSAndroid Build Coastguard Worker   fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n",
131*795d594fSAndroid Build Coastguard Worker           insnsOff, accessor.InsnsSizeInCodeUnits() * 2,
132*795d594fSAndroid Build Coastguard Worker           className.get(), methodName, typeDesc, fileName, first_line);
133*795d594fSAndroid Build Coastguard Worker 
134*795d594fSAndroid Build Coastguard Worker   free(typeDesc);
135*795d594fSAndroid Build Coastguard Worker }
136*795d594fSAndroid Build Coastguard Worker 
137*795d594fSAndroid Build Coastguard Worker /*
138*795d594fSAndroid Build Coastguard Worker  * Runs through all direct and virtual methods in the class.
139*795d594fSAndroid Build Coastguard Worker  */
dumpClass(const DexFile * pDexFile,u4 idx)140*795d594fSAndroid Build Coastguard Worker void dumpClass(const DexFile* pDexFile, u4 idx) {
141*795d594fSAndroid Build Coastguard Worker   const dex::ClassDef& class_def = pDexFile->GetClassDef(idx);
142*795d594fSAndroid Build Coastguard Worker 
143*795d594fSAndroid Build Coastguard Worker   const char* fileName = nullptr;
144*795d594fSAndroid Build Coastguard Worker   if (class_def.source_file_idx_.IsValid()) {
145*795d594fSAndroid Build Coastguard Worker     fileName = pDexFile->GetStringData(class_def.source_file_idx_);
146*795d594fSAndroid Build Coastguard Worker   }
147*795d594fSAndroid Build Coastguard Worker 
148*795d594fSAndroid Build Coastguard Worker   ClassAccessor accessor(*pDexFile, class_def);
149*795d594fSAndroid Build Coastguard Worker   for (const ClassAccessor::Method& method : accessor.GetMethods()) {
150*795d594fSAndroid Build Coastguard Worker     dumpMethod(pDexFile,
151*795d594fSAndroid Build Coastguard Worker                fileName,
152*795d594fSAndroid Build Coastguard Worker                method.GetIndex(),
153*795d594fSAndroid Build Coastguard Worker                method.GetAccessFlags(),
154*795d594fSAndroid Build Coastguard Worker                method.GetCodeItem(),
155*795d594fSAndroid Build Coastguard Worker                method.GetCodeItemOffset());
156*795d594fSAndroid Build Coastguard Worker   }
157*795d594fSAndroid Build Coastguard Worker }
158*795d594fSAndroid Build Coastguard Worker 
159*795d594fSAndroid Build Coastguard Worker /*
160*795d594fSAndroid Build Coastguard Worker  * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
161*795d594fSAndroid Build Coastguard Worker  */
processFile(const char * fileName)162*795d594fSAndroid Build Coastguard Worker static int processFile(const char* fileName) {
163*795d594fSAndroid Build Coastguard Worker   // If the file is not a .dex file, the function tries .zip/.jar/.apk files,
164*795d594fSAndroid Build Coastguard Worker   // all of which are Zip archives with "classes.dex" inside.
165*795d594fSAndroid Build Coastguard Worker   static constexpr bool kVerifyChecksum = true;
166*795d594fSAndroid Build Coastguard Worker   std::string content;
167*795d594fSAndroid Build Coastguard Worker   // TODO: add an api to android::base to read a std::vector<uint8_t>.
168*795d594fSAndroid Build Coastguard Worker   if (!android::base::ReadFileToString(fileName, &content)) {
169*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << "ReadFileToString failed";
170*795d594fSAndroid Build Coastguard Worker     return -1;
171*795d594fSAndroid Build Coastguard Worker   }
172*795d594fSAndroid Build Coastguard Worker   std::vector<std::unique_ptr<const DexFile>> dex_files;
173*795d594fSAndroid Build Coastguard Worker   DexFileLoaderErrorCode error_code;
174*795d594fSAndroid Build Coastguard Worker   std::string error_msg;
175*795d594fSAndroid Build Coastguard Worker   DexFileLoader dex_file_loader(
176*795d594fSAndroid Build Coastguard Worker       reinterpret_cast<const uint8_t*>(content.data()), content.size(), fileName);
177*795d594fSAndroid Build Coastguard Worker   if (!dex_file_loader.Open(
178*795d594fSAndroid Build Coastguard Worker           /*verify=*/true, kVerifyChecksum, &error_code, &error_msg, &dex_files)) {
179*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << error_msg;
180*795d594fSAndroid Build Coastguard Worker     return -1;
181*795d594fSAndroid Build Coastguard Worker   }
182*795d594fSAndroid Build Coastguard Worker 
183*795d594fSAndroid Build Coastguard Worker   // Success. Iterate over all dex files found in given file.
184*795d594fSAndroid Build Coastguard Worker   fprintf(gOutFile, "#%s\n", fileName);
185*795d594fSAndroid Build Coastguard Worker   for (size_t i = 0; i < dex_files.size(); i++) {
186*795d594fSAndroid Build Coastguard Worker     // Iterate over all classes in one dex file.
187*795d594fSAndroid Build Coastguard Worker     const DexFile* pDexFile = dex_files[i].get();
188*795d594fSAndroid Build Coastguard Worker     const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
189*795d594fSAndroid Build Coastguard Worker     for (u4 idx = 0; idx < classDefsSize; idx++) {
190*795d594fSAndroid Build Coastguard Worker       dumpClass(pDexFile, idx);
191*795d594fSAndroid Build Coastguard Worker     }
192*795d594fSAndroid Build Coastguard Worker   }
193*795d594fSAndroid Build Coastguard Worker   return 0;
194*795d594fSAndroid Build Coastguard Worker }
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker /*
197*795d594fSAndroid Build Coastguard Worker  * Shows usage.
198*795d594fSAndroid Build Coastguard Worker  */
usage()199*795d594fSAndroid Build Coastguard Worker static void usage() {
200*795d594fSAndroid Build Coastguard Worker   LOG(ERROR) << "Copyright (C) 2007 The Android Open Source Project\n";
201*795d594fSAndroid Build Coastguard Worker   LOG(ERROR) << gProgName << ": [-m p.c.m] [-o outfile] dexfile...";
202*795d594fSAndroid Build Coastguard Worker   LOG(ERROR) << "";
203*795d594fSAndroid Build Coastguard Worker }
204*795d594fSAndroid Build Coastguard Worker 
205*795d594fSAndroid Build Coastguard Worker /*
206*795d594fSAndroid Build Coastguard Worker  * Main driver of the dexlist utility.
207*795d594fSAndroid Build Coastguard Worker  */
dexlistDriver(int argc,char ** argv)208*795d594fSAndroid Build Coastguard Worker int dexlistDriver(int argc, char** argv) {
209*795d594fSAndroid Build Coastguard Worker   // Reset options.
210*795d594fSAndroid Build Coastguard Worker   bool wantUsage = false;
211*795d594fSAndroid Build Coastguard Worker   memset(&gOptions, 0, sizeof(gOptions));
212*795d594fSAndroid Build Coastguard Worker 
213*795d594fSAndroid Build Coastguard Worker   // Parse all arguments.
214*795d594fSAndroid Build Coastguard Worker   while (true) {
215*795d594fSAndroid Build Coastguard Worker     const int ic = getopt(argc, argv, "o:m:");
216*795d594fSAndroid Build Coastguard Worker     if (ic < 0) {
217*795d594fSAndroid Build Coastguard Worker       break;  // done
218*795d594fSAndroid Build Coastguard Worker     }
219*795d594fSAndroid Build Coastguard Worker     switch (ic) {
220*795d594fSAndroid Build Coastguard Worker       case 'o':  // output file
221*795d594fSAndroid Build Coastguard Worker         gOptions.outputFileName = optarg;
222*795d594fSAndroid Build Coastguard Worker         break;
223*795d594fSAndroid Build Coastguard Worker       case 'm':
224*795d594fSAndroid Build Coastguard Worker         // If -m p.c.m is given, then find all instances of the
225*795d594fSAndroid Build Coastguard Worker         // fully-qualified method name. This isn't really what
226*795d594fSAndroid Build Coastguard Worker         // dexlist is for, but it's easy to do it here.
227*795d594fSAndroid Build Coastguard Worker         {
228*795d594fSAndroid Build Coastguard Worker           gOptions.argCopy = strdup(optarg);
229*795d594fSAndroid Build Coastguard Worker           char* meth = strrchr(gOptions.argCopy, '.');
230*795d594fSAndroid Build Coastguard Worker           if (meth == nullptr) {
231*795d594fSAndroid Build Coastguard Worker             LOG(ERROR) << "Expected: package.Class.method";
232*795d594fSAndroid Build Coastguard Worker             wantUsage = true;
233*795d594fSAndroid Build Coastguard Worker           } else {
234*795d594fSAndroid Build Coastguard Worker             *meth = '\0';
235*795d594fSAndroid Build Coastguard Worker             gOptions.classToFind = gOptions.argCopy;
236*795d594fSAndroid Build Coastguard Worker             gOptions.methodToFind = meth + 1;
237*795d594fSAndroid Build Coastguard Worker           }
238*795d594fSAndroid Build Coastguard Worker         }
239*795d594fSAndroid Build Coastguard Worker         break;
240*795d594fSAndroid Build Coastguard Worker       default:
241*795d594fSAndroid Build Coastguard Worker         wantUsage = true;
242*795d594fSAndroid Build Coastguard Worker         break;
243*795d594fSAndroid Build Coastguard Worker     }  // switch
244*795d594fSAndroid Build Coastguard Worker   }  // while
245*795d594fSAndroid Build Coastguard Worker 
246*795d594fSAndroid Build Coastguard Worker   // Detect early problems.
247*795d594fSAndroid Build Coastguard Worker   if (optind == argc) {
248*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << "No file specified";
249*795d594fSAndroid Build Coastguard Worker     wantUsage = true;
250*795d594fSAndroid Build Coastguard Worker   }
251*795d594fSAndroid Build Coastguard Worker   if (wantUsage) {
252*795d594fSAndroid Build Coastguard Worker     usage();
253*795d594fSAndroid Build Coastguard Worker     free(gOptions.argCopy);
254*795d594fSAndroid Build Coastguard Worker     return 2;
255*795d594fSAndroid Build Coastguard Worker   }
256*795d594fSAndroid Build Coastguard Worker 
257*795d594fSAndroid Build Coastguard Worker   // Open alternative output file.
258*795d594fSAndroid Build Coastguard Worker   if (gOptions.outputFileName) {
259*795d594fSAndroid Build Coastguard Worker     gOutFile = fopen(gOptions.outputFileName, "we");
260*795d594fSAndroid Build Coastguard Worker     if (!gOutFile) {
261*795d594fSAndroid Build Coastguard Worker       PLOG(ERROR) << "Can't open " << gOptions.outputFileName;
262*795d594fSAndroid Build Coastguard Worker       free(gOptions.argCopy);
263*795d594fSAndroid Build Coastguard Worker       return 1;
264*795d594fSAndroid Build Coastguard Worker     }
265*795d594fSAndroid Build Coastguard Worker   }
266*795d594fSAndroid Build Coastguard Worker 
267*795d594fSAndroid Build Coastguard Worker   // Process all files supplied on command line. If one of them fails we
268*795d594fSAndroid Build Coastguard Worker   // continue on, only returning a failure at the end.
269*795d594fSAndroid Build Coastguard Worker   int result = 0;
270*795d594fSAndroid Build Coastguard Worker   while (optind < argc) {
271*795d594fSAndroid Build Coastguard Worker     result |= processFile(argv[optind++]);
272*795d594fSAndroid Build Coastguard Worker   }  // while
273*795d594fSAndroid Build Coastguard Worker 
274*795d594fSAndroid Build Coastguard Worker   free(gOptions.argCopy);
275*795d594fSAndroid Build Coastguard Worker   return result != 0 ? 1 : 0;
276*795d594fSAndroid Build Coastguard Worker }
277*795d594fSAndroid Build Coastguard Worker 
278*795d594fSAndroid Build Coastguard Worker }  // namespace art
279*795d594fSAndroid Build Coastguard Worker 
main(int argc,char ** argv)280*795d594fSAndroid Build Coastguard Worker int main(int argc, char** argv) {
281*795d594fSAndroid Build Coastguard Worker   // Output all logging to stderr.
282*795d594fSAndroid Build Coastguard Worker   android::base::SetLogger(android::base::StderrLogger);
283*795d594fSAndroid Build Coastguard Worker   art::MemMap::Init();
284*795d594fSAndroid Build Coastguard Worker 
285*795d594fSAndroid Build Coastguard Worker   return art::dexlistDriver(argc, argv);
286*795d594fSAndroid Build Coastguard Worker }
287*795d594fSAndroid Build Coastguard Worker 
288