main.java revision a5fb30849ded98084d9ba486c43d7a8c41b55ca3
1/*
2 * Copyright (C) 2007 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
17package org.jf.baksmali;
18
19import org.apache.commons.cli.*;
20import org.jf.dexlib.DexFile;
21import org.jf.dexlib.Util.Deodexerant;
22
23import java.io.File;
24import java.io.InputStream;
25import java.io.IOException;
26import java.util.Properties;
27
28public class main {
29
30    public static final String VERSION;
31
32    private static final Options options;
33
34    static {
35        options = new Options();
36        buildOptions();
37
38        InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
39        Properties properties = new Properties();
40        String version = "(unknown)";
41        try {
42            properties.load(templateStream);
43            version = properties.getProperty("application.version");
44        } catch (IOException ex) {
45        }
46        VERSION = version;
47    }
48
49    /**
50     * This class is uninstantiable.
51     */
52    private main() {
53    }
54
55    /**
56     * Run!
57     */
58    public static void main(String[] args) {
59        CommandLineParser parser = new PosixParser();
60        CommandLine commandLine;
61
62        try {
63            commandLine = parser.parse(options, args);
64        } catch (ParseException ex) {
65            usage();
66            return;
67        }
68
69        boolean disassemble = true;
70        boolean doDump = false;
71        boolean write = false;
72        boolean sort = false;
73        boolean fixRegisters = false;
74        boolean noParameterRegisters = false;
75        boolean useLocalsDirective = false;
76
77
78        String outputDirectory = "out";
79        String dumpFileName = null;
80        String outputDexFileName = null;
81        String inputDexFileName = null;
82        String deodexerantHost = null;
83        int deodexerantPort = 0;
84
85        String[] remainingArgs = commandLine.getArgs();
86
87        if (commandLine.hasOption("v")) {
88            version();
89            return;
90        }
91
92        if (commandLine.hasOption("?")) {
93            usage();
94            return;
95        }
96
97        if (remainingArgs.length != 1) {
98            usage();
99            return;
100        }
101
102        inputDexFileName = remainingArgs[0];
103
104        if (commandLine.hasOption("n")) {
105            disassemble = false;
106        }
107
108        if (commandLine.hasOption("d")) {
109            doDump = true;
110            dumpFileName = commandLine.getOptionValue("d", inputDexFileName + ".dump");
111        }
112
113        if (commandLine.hasOption("w")) {
114            write = true;
115            outputDexFileName = commandLine.getOptionValue("w");
116        }
117
118        if (commandLine.hasOption("o")) {
119            outputDirectory = commandLine.getOptionValue("o");
120        }
121
122        if (commandLine.hasOption("s")) {
123            sort = true;
124        }
125
126        if (commandLine.hasOption("f")) {
127            fixRegisters = true;
128        }
129
130        if (commandLine.hasOption("p")) {
131            noParameterRegisters = true;
132        }
133
134        if (commandLine.hasOption("l")) {
135            useLocalsDirective = true;
136        }
137
138        if (commandLine.hasOption("x")) {
139            String deodexerantAddress = commandLine.getOptionValue("x");
140            String[] parts = deodexerantAddress.split(":");
141            if (parts.length != 2) {
142                System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
143                System.exit(1);
144            }
145
146            deodexerantHost = parts[0];
147            if (deodexerantHost.length() == 0) {
148                deodexerantHost = "localhost";
149            }
150            try {
151                deodexerantPort = Integer.parseInt(parts[1]);
152            } catch (NumberFormatException ex) {
153                System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
154                System.exit(1);
155            }
156        }
157
158        try {
159            File dexFileFile = new File(inputDexFileName);
160            if (!dexFileFile.exists()) {
161                System.err.println("Can't find the file " + inputDexFileName);
162                System.exit(1);
163            }
164
165            //Read in and parse the dex file
166            DexFile dexFile = new DexFile(dexFileFile, !fixRegisters);
167
168            Deodexerant deodexerant = null;
169
170
171            if (deodexerantHost != null) {
172                if (!dexFile.isOdex()) {
173                    System.err.println("-x cannot be used with a normal dex file. Ignoring -x");
174                }
175                deodexerant = new Deodexerant(dexFile, deodexerantHost, deodexerantPort);
176            }
177
178            if (dexFile.isOdex()) {
179                if (doDump) {
180                    System.err.println("-d cannot be used with on odex file. Ignoring -d");
181                }
182                if (write) {
183                    System.err.println("-w cannot be used with an odex file. Ignoring -w");
184                }
185            }
186
187            if (disassemble) {
188                baksmali.disassembleDexFile(dexFile, deodexerant, outputDirectory, noParameterRegisters,
189                        useLocalsDirective);
190            }
191
192            if ((doDump || write) && !dexFile.isOdex()) {
193                try
194                {
195                    dump.dump(dexFile, dumpFileName, outputDexFileName, sort);
196                }catch (IOException ex) {
197                    System.err.println("Error occured while writing dump file");
198                    ex.printStackTrace();
199                }
200            }
201        } catch (RuntimeException ex) {
202            System.err.println("\n\nUNEXPECTED TOP-LEVEL EXCEPTION:");
203            ex.printStackTrace();
204            System.exit(1);
205        } catch (Throwable ex) {
206            System.err.println("\n\nUNEXPECTED TOP-LEVEL ERROR:");
207            ex.printStackTrace();
208            System.exit(1);
209        }
210    }
211
212    /**
213     * Prints the usage message.
214     */
215    private static void usage() {
216        HelpFormatter formatter = new HelpFormatter();
217        formatter.printHelp("java -jar baksmali.jar [options] <dex-file>",
218                "disassembles and/or dumps a dex file", options, "");
219    }
220
221    /**
222     * Prints the version message.
223     */
224    private static void version() {
225        System.out.println("baksmali " + VERSION + " (http://smali.googlecode.com)");
226        System.out.println("Copyright (C) 2009 Ben Gruver");
227        System.out.println("BSD license (http://www.opensource.org/licenses/bsd-license.php)");
228        System.exit(0);
229    }
230
231    private static void buildOptions() {
232        Option versionOption = OptionBuilder.withLongOpt("version")
233                .withDescription("prints the version then exits")
234                .create("v");
235
236        Option helpOption = OptionBuilder.withLongOpt("help")
237                .withDescription("prints the help message then exits")
238                .create("?");
239
240        Option noDisassemblyOption = OptionBuilder.withLongOpt("no-disassembly")
241                .withDescription("suppresses the output of the disassembly")
242                .create("n");
243
244        Option dumpOption = OptionBuilder.withLongOpt("dump-to")
245                .withDescription("dumps the given dex file into a single annotated dump file named FILE" +
246                        " (<dexfile>.dump by default), along with the normal disassembly.")
247                .hasOptionalArg()
248                .withArgName("FILE")
249                .create("d");
250
251        Option writeDexOption = OptionBuilder.withLongOpt("write-dex")
252                .withDescription("additionally rewrites the input dex file to FILE")
253                .hasArg()
254                .withArgName("FILE")
255                .create("w");
256
257        Option outputDirOption = OptionBuilder.withLongOpt("output")
258                .withDescription("the directory where the disassembled files will be placed. The default is out")
259                .hasArg()
260                .withArgName("DIR")
261                .create("o");
262
263        Option sortOption = OptionBuilder.withLongOpt("sort")
264                .withDescription("sort the items in the dex file into a canonical order before dumping/writing")
265                .create("s");
266
267        Option fixSignedRegisterOption = OptionBuilder.withLongOpt("fix-signed-registers")
268                .withDescription("when dumping or rewriting, fix any registers in the debug info that are encoded as" +
269                        " a signed value")
270                .create("f");
271
272        Option noParameterRegistersOption = OptionBuilder.withLongOpt("no-parameter-registers")
273                .withDescription("use the v<n> syntax instead of the p<n> syntax for registers mapped to method" +
274                        " parameters")
275                .create("p");
276
277        Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
278                .withDescription("connect to deodexerant on the specified HOST:PORT, and deodex the input odex"
279                        + " file. This option is ignored if the input file is a dex file instead of an odex file")
280                .hasArg()
281                .withArgName("HOST:PORT")
282                .create("x");
283
284        Option useLocalsOption = OptionBuilder.withLongOpt("use-locals")
285                .withDescription("output the .locals directive with the number of non-parameter registers, instead of" +
286                        " the .register directive with the total number of register")
287                .create("l");
288
289        OptionGroup dumpCommand = new OptionGroup();
290
291        options.addOption(versionOption);
292        options.addOption(helpOption);
293        options.addOption(dumpOption);
294        options.addOption(noDisassemblyOption);
295        options.addOption(writeDexOption);
296        options.addOption(outputDirOption);
297        options.addOption(sortOption);
298        options.addOption(fixSignedRegisterOption);
299        options.addOption(noParameterRegistersOption);
300        options.addOption(deodexerantOption);
301        options.addOption(useLocalsOption);
302    }
303}