1   /*
2  * Copyright (C) 2016 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  * Main driver of the dexlayout utility.
17  *
18  * This is a tool to read dex files into an internal representation,
19  * reorganize the representation, and emit dex files with a better
20  * file layout.
21  */
22 
23 #include "dexlayout.h"
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 
32 #include <android-base/logging.h>
33 
34 #include "base/logging.h"  // For InitLogging.
35 #include "base/mem_map.h"
36 #include "profile/profile_compilation_info.h"
37 
38 namespace art {
39 
40 static const char* kProgramName = "dexlayout";
41 
42 /*
43  * Shows usage.
44  */
Usage()45 static void Usage() {
46   LOG(ERROR) << "Copyright (C) 2016 The Android Open Source Project\n";
47   LOG(ERROR) << kProgramName
48              << ": [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
49                 " [-s] [-t] [-u] [-v] [-w directory] dexfile...\n";
50   LOG(ERROR) << " -a : display annotations";
51   LOG(ERROR) << " -b : build dex_ir";
52   LOG(ERROR) << " -c : verify checksum and exit";
53   LOG(ERROR) << " -d : disassemble code sections";
54   LOG(ERROR) << " -e : display exported items only";
55   LOG(ERROR) << " -f : display summary information from file header";
56   LOG(ERROR) << " -h : display file header details";
57   LOG(ERROR) << " -i : ignore checksum failures";
58   LOG(ERROR) << " -l : output layout, either 'plain' or 'xml'";
59   LOG(ERROR) << " -o : output file name (defaults to stdout)";
60   LOG(ERROR) << " -p : profile file name (defaults to no profile)";
61   LOG(ERROR) << " -s : visualize reference pattern";
62   LOG(ERROR) << " -t : display file section sizes";
63   LOG(ERROR) << " -u : update dex checksums";
64   LOG(ERROR) << " -v : verify output file is canonical to input (IR level comparison)";
65   LOG(ERROR) << " -w : output dex directory";
66   LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'";
67 }
68 
Abort(const char * msg)69 NO_RETURN static void Abort(const char* msg) {
70   LOG(ERROR) << msg;
71   exit(1);
72 }
73 
74 /*
75  * Main driver of the dexlayout utility.
76  */
DexlayoutDriver(int argc,char ** argv)77 int DexlayoutDriver(int argc, char** argv) {
78   // Art specific set up.
79   InitLogging(argv, Abort);
80   MemMap::Init();
81 
82   Options options;
83   options.dump_ = true;
84   options.verbose_ = true;
85   bool want_usage = false;
86 
87   // Parse all arguments.
88   while (true) {
89     const int ic = getopt(argc, argv, "abcdefghil:o:p:stuvw:x:");
90     if (ic < 0) {
91       break;  // done
92     }
93     switch (ic) {
94       case 'a':  // display annotations
95         options.show_annotations_ = true;
96         break;
97       case 'b':  // build dex_ir
98         options.build_dex_ir_ = true;
99         break;
100       case 'c':  // verify the checksum then exit
101         options.checksum_only_ = true;
102         break;
103       case 'd':  // disassemble Dalvik instructions
104         options.disassemble_ = true;
105         break;
106       case 'e':  // exported items only
107         options.exports_only_ = true;
108         break;
109       case 'f':  // display outer file header
110         options.show_file_headers_ = true;
111         break;
112       case 'h':  // display section headers, i.e. all meta-data
113         options.show_section_headers_ = true;
114         break;
115       case 'i':  // continue even if checksum is bad
116         options.ignore_bad_checksum_ = true;
117         break;
118       case 'l':  // layout
119         if (strcmp(optarg, "plain") == 0) {
120           options.output_format_ = kOutputPlain;
121         } else if (strcmp(optarg, "xml") == 0) {
122           options.output_format_ = kOutputXml;
123           options.verbose_ = false;
124         } else {
125           want_usage = true;
126         }
127         break;
128       case 'o':  // output file
129         options.output_file_name_ = optarg;
130         break;
131       case 'p':  // profile file
132         options.profile_file_name_ = optarg;
133         break;
134       case 's':  // visualize access pattern
135         options.visualize_pattern_ = true;
136         options.verbose_ = false;
137         break;
138       case 't':  // display section statistics
139         options.show_section_statistics_ = true;
140         options.verbose_ = false;
141         break;
142       case 'u':  // update checksum
143         options.update_checksum_ = true;
144         break;
145       case 'v':  // verify output
146         options.verify_output_ = true;
147         break;
148       case 'w':  // output dex files directory
149         options.output_dex_directory_ = optarg;
150         break;
151       case 'x':  // compact dex level
152         if (strcmp(optarg, "none") == 0) {
153           options.compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone;
154         } else if (strcmp(optarg, "fast") == 0) {
155           options.compact_dex_level_ = CompactDexLevel::kCompactDexLevelFast;
156         } else {
157           want_usage = true;
158         }
159         break;
160       default:
161         want_usage = true;
162         break;
163     }  // switch
164   }  // while
165 
166   // Detect early problems.
167   if (optind == argc) {
168     LOG(ERROR) << "no file specified";
169     want_usage = true;
170   }
171   if (options.checksum_only_ && options.ignore_bad_checksum_) {
172     LOG(ERROR) << "Can't specify both -c and -i";
173     want_usage = true;
174   }
175   if (want_usage) {
176     Usage();
177     return 2;
178   }
179 
180   // Open alternative output file.
181   FILE* out_file = stdout;
182   if (options.output_file_name_) {
183     out_file = fopen(options.output_file_name_, "w");
184     if (!out_file) {
185       PLOG(ERROR) << "Can't open " << options.output_file_name_;
186       return 1;
187     }
188   }
189 
190   // Open profile file.
191   std::unique_ptr<ProfileCompilationInfo> profile_info;
192   if (options.profile_file_name_) {
193 #ifdef _WIN32
194     int flags = O_RDONLY;
195 #else
196     int flags = O_RDONLY | O_CLOEXEC;
197 #endif
198     int profile_fd = open(options.profile_file_name_, flags);
199     if (profile_fd < 0) {
200       PLOG(ERROR) << "Can't open " << options.profile_file_name_;
201       return 1;
202     }
203     profile_info.reset(new ProfileCompilationInfo());
204     if (!profile_info->Load(profile_fd)) {
205       LOG(ERROR) << "Can't read profile info from " << options.profile_file_name_;
206       return 1;
207     }
208   }
209   PLOG(INFO) << "After opening profile file";
210 
211   // Create DexLayout instance.
212   DexLayout dex_layout(options, profile_info.get(), out_file, /*header=*/ nullptr);
213 
214   // Process all files supplied on command line.
215   int result = 0;
216   while (optind < argc) {
217     result |= dex_layout.ProcessFile(argv[optind++]);
218   }  // while
219 
220   if (options.output_file_name_) {
221     CHECK(out_file != nullptr && out_file != stdout);
222     fclose(out_file);
223   }
224 
225   return result != 0 ? 1 : 0;
226 }
227 
228 }  // namespace art
229 
main(int argc,char ** argv)230 int main(int argc, char** argv) {
231   // Output all logging to stderr.
232   android::base::SetLogger(android::base::StderrLogger);
233 
234   return art::DexlayoutDriver(argc, argv);
235 }
236