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